Import modules

In [2]:
%%time
%load_ext memory_profiler

#___________________________
# basics
import datetime
import os, glob, sys, gc
# import warnings
# warnings.filterwarnings('ignore', '.*invalid value encountered in true_divide.*', )

#___________________________
# To follow computations
from dask.diagnostics import ProgressBar
pbar = ProgressBar(minimum=10)
pbar.register()
#pbar.unregister()

#___________________________
# xarray numpy...
import numpy as np
import xarray as xr
xr.set_options(keep_attrs=True)
from fast_histogram import histogram2d
from scipy import stats

#___________________________
# To plot
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import ListedColormap

import cartopy.crs       as ccrs
import cartopy.feature as cfeature
import cartopy

import cmcrameri

import tol_colors

plt.figure()
mpl.rcParams['savefig.dpi'] = 300
mpl.rcParams['figure.dpi'] = 200 # dpi for figure shonw in the notebook (ie. size of the figure)

#___________________________
# Extra
import pickle
CPU times: user 1.88 s, sys: 1.3 s, total: 3.18 s
Wall time: 3.15 s
<Figure size 432x288 with 0 Axes>

Starters

In [3]:
%%time
%%memit -c
print(datetime.datetime.now())

dirout = '24-12-17-figures-for-storyline-v1/'
if not os.path.isdir(dirout) : os.mkdir(dirout)

dirshared = '/mnt/reef-ns1002k/daco/MY_JUPYTER_NOTEBOOKS/OceanICU/SHARED-DATAS/'

netcdfdir = dirout+'netcdf_files/'
if not os.path.isdir(netcdfdir) : os.mkdir(netcdfdir)

sys.stdout.echo = open(dirout+'stdout.txt', 'w')
sys.stderr.echo = open(dirout+'stderr.txt', 'w')
2025-05-08 19:07:18.448702
peak memory: 417.69 MiB, increment: 166.02 MiB
CPU times: user 145 ms, sys: 0 ns, total: 145 ms
Wall time: 246 ms

Main parameters

In [4]:
%%time
%%memit -c

print(datetime.datetime.now())

cm2in = 1/2.54 # to convert from cm to inches

# Set custom font sizes
plt.rcParams['font.size'] = 8 # Default font size: 10.0
plt.rcParams['axes.titlesize'] = 8 # Default axes title font size: large
plt.rcParams['axes.labelsize'] = 6 # Default axes label font size: large
plt.rcParams['xtick.labelsize'] = 6 # Default tick labels font size: medium
plt.rcParams['ytick.labelsize'] = 6 # Default tick labels font size: medium
plt.rcParams['legend.fontsize'] = 6 # Default legend font size: medium
plt.rcParams['figure.titlesize'] = 10 # Default figure title font size: large

plt.rcParams['lines.linewidth'] = 1.
plt.rcParams['grid.linewidth'] = .5
plt.rcParams['axes.linewidth'] = .5
plt.rcParams['xtick.major.width'] = .5
plt.rcParams['xtick.minor.width'] = .5
plt.rcParams['ytick.major.width'] = .5
plt.rcParams['ytick.minor.width'] = .5

kwopends=dict(use_cftime=True, decode_times=None,
              decode_cf=True, decode_coords=True)
kwopenmfds = dict(combine='by_coords', parallel=True, 
                  use_cftime=True, decode_times=None,
                  decode_cf=True, decode_coords=True)

rename_dict = {
    "x": "i",
    "y": "j",
    "lat": "latitude", 
    "lon": "longitude",
    "nav_lat": "latitude", 
    "nav_lon": "longitude",
    'lev': 'depth', 
    'deptht': 'depth', 
    'olevel': 'depth', 
    "Depth":"depth"
}

# esm_color_dict = {
#     'MPI-ESM1-2-LR': tol_colors.tol_cset(colorset="bright").blue, 
#     'ACCESS-ESM1-5': tol_colors.tol_cset(colorset="bright").red,
#     'IPSL-CM6A-LR' : tol_colors.tol_cset(colorset="bright").green,
#     'MIROC-ES2L'   : tol_colors.tol_cset(colorset="bright").yellow,
#     'NorESM2-LM'   : tol_colors.tol_cset(colorset="bright").cyan
# }
esm_color_dict = {
    'MPI-ESM1-2-LR': cmcrameri.cm.batlowS.colors[0],
    'ACCESS-ESM1-5': cmcrameri.cm.batlowS.colors[1],
    'IPSL-CM6A-LR' : cmcrameri.cm.batlowS.colors[2],
    'MIROC-ES2L'   : cmcrameri.cm.batlowS.colors[3],
    'NorESM2-LM'   : cmcrameri.cm.batlowS.colors[4]
}

esm_name_dict = {
    'MPI-ESM1-2-LR': 'MPI-ESM1.2-LR',
    'ACCESS-ESM1-5': 'ACCESS-ESM1.5',
    'IPSL-CM6A-LR' : 'IPSL-CM6A-LR' ,
    'MIROC-ES2L'   : 'MIROC-ES2L'   ,
    'NorESM2-LM'   : 'NorESM2-LM'   , 
    'CanESM5'      : 'CanESM5'      , 
    'CNRM-ESM2-1'  : 'CNRM-ESM2-1'  ,
    'GFDL-ESM4'    : 'GFDL-ESM4'    ,
    'UKESM1-0-LL'  : 'UKESM1-0-LL'
}

picontrol_refyear_dict = {
    'MPI-ESM1-2-LR': 1850,
    'ACCESS-ESM1-5':  161,
    'IPSL-CM6A-LR' : 1910,
    'CanESM5'      : 5201,
    'MIROC-ES2L'   : 1850,
    'NorESM2-LM'   : 1600, 
    'CNRM-ESM2-1'  : 1850, 
    'GFDL-CM4'     :  101,
    'GFDL-ESM4'    :  101,
    'UKESM1-0-LL'  : 2250
}

#--------------------
# Dictionnaries for ESGF
#--------------------

variant_dict = {
    'MPI-ESM1-2-LR': 'r1i1p1f1',
    'ACCESS-ESM1-5': 'r1i1p1f1',
    'IPSL-CM6A-LR' : 'r1i1p1f1',
    'CanESM5'      : 'r1i1p1f1',
    'MIROC-ES2L'   : 'r1i1p1f2', 
    'CNRM-ESM2-1'  : 'r1i1p1f2', 
    'GFDL-CM4'     : 'r1i1p1f1',
    'GFDL-ESM4'    : 'r1i1p1f1',
    'UKESM1-0-LL'  : 'r1i1p1f2'
}

version_dict = {
    'piControl': {
        'thetao':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }, 
        'so':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }, 
        'o2':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }
    }, 
    'historical': {
        'thetao':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20180803',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20181206', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20190726',
            'UKESM1-0-LL'  : 'v20190627'
        }, 
        'so':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20180803',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20181206', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20190726',
            'UKESM1-0-LL'  : 'v20190627'
        }, 
        'o2':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20180803',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20181206', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20190726',
            'UKESM1-0-LL'  : 'v20190627'
        },
        'areacello':{
            'MPI-ESM1-2-LR': 'latest',
            'ACCESS-ESM1-5': 'latest',
            'IPSL-CM6A-LR' : 'latest',
            'CanESM5'      : 'latest',
            'MIROC-ES2L'   : 'latest',
            'CNRM-ESM2-1'  : 'latest', 
            'GFDL-CM4'     : 'latest',
            'GFDL-ESM4'    : 'latest',
            'UKESM1-0-LL'  : 'v20190705'
        }        

    }, 
    'ssp585': {
        'thetao':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }, 
        'so':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }, 
        'o2':{
            'MPI-ESM1-2-LR': 'v20190710',
            'ACCESS-ESM1-5': 'v20191115',
            'IPSL-CM6A-LR' : 'v20190903',
            'CanESM5'      : 'v20190429',
            'MIROC-ES2L'   : 'v20190823',
            'CNRM-ESM2-1'  : 'v20191021', 
            'GFDL-CM4'     : 'v20180701',
            'GFDL-ESM4'    : 'v20180701',
            'UKESM1-0-LL'  : 'v20190726'
        }
    }
}

grid_dict = {
    'thetao':{
        'MPI-ESM1-2-LR': 'gn',
        'ACCESS-ESM1-5': 'gn',
        'IPSL-CM6A-LR' : 'gn',
        'CanESM5'      : 'gn',
        'MIROC-ES2L'   : 'gn',
        'CNRM-ESM2-1'  : 'gn', 
        'GFDL-CM4'     : 'gr',
        'GFDL-ESM4'    : 'gr',
        'UKESM1-0-LL'  : 'gn'
    }, 
    'so':{
        'MPI-ESM1-2-LR': 'gn',
        'ACCESS-ESM1-5': 'gn',
        'IPSL-CM6A-LR' : 'gn',
        'CanESM5'      : 'gn',
        'MIROC-ES2L'   : 'gn',
        'CNRM-ESM2-1'  : 'gn', 
        'GFDL-CM4'     : 'gr',
        'GFDL-ESM4'    : 'gr',
        'UKESM1-0-LL'  : 'gn'
    }, 
    'o2':{
        'MPI-ESM1-2-LR': 'gn',
        'ACCESS-ESM1-5': 'gn',
        'IPSL-CM6A-LR' : 'gn',
        'CanESM5'      : 'gn',
        'MIROC-ES2L'   : 'gn',
        'CNRM-ESM2-1'  : 'gn', 
        'GFDL-CM4'     : 'gr',
        'GFDL-ESM4'    : 'gr',
        'UKESM1-0-LL'  : 'gn'
    }, 
    'areacello':{
        'MPI-ESM1-2-LR': 'gn',
        'ACCESS-ESM1-5': 'gn',
        'IPSL-CM6A-LR' : 'gn',
        'CanESM5'      : 'gn',
        'MIROC-ES2L'   : 'gn',
        'CNRM-ESM2-1'  : 'gn', 
        'GFDL-CM4'     : 'gr',
        'GFDL-ESM4'    : 'gr',
        'UKESM1-0-LL'  : 'gn'
    }
} 
2025-05-08 19:07:18.719863
peak memory: 419.91 MiB, increment: 184.55 MiB
CPU times: user 147 ms, sys: 0 ns, total: 147 ms
Wall time: 254 ms

Define some functions

Preparation of data

In [5]:
def shift_180_lon(zwda, verbose=False): 
    if verbose: print("func: shift_180_lon")
    
    try: 
        if not np.nanmin(zwda['longitude']) < -150: 
            zwda['longitude'] = (zwda['longitude'] + 180) % 360 - 180
            addtxt=str(datetime.datetime.now())+' shift_180_lon to get longitude from -180 to 180'
            try: zwda.attrs['history'] =  addtxt + ' ; '+zwda.attrs['history']
            except: zwda.attrs['history'] =  addtxt             
        #
    except: print('WARNING! longitude likely not shifted')
    return zwda
#

def rename_vars_dims_coords(ds, rename_dict, verbose=False):
    """
    Renames variables, dimensions, and coordinates in an xarray Dataset according to the provided rename dictionary.

    Parameters:
    -----------
    ds : xr.Dataset
        The xarray Dataset to be renamed.
    rename_dict : Dict[str, str]
        Dictionary containing the variable, dimension, or coordinate names to be renamed. 
        The keys represent the original names, and the values represent the new names.
    verbose : bool, optional
        If True, prints the function name at the start and end of execution (default is False).
    
    Returns:
    --------
    xr.Dataset
        A new xarray Dataset with variables, dimensions, and coordinates renamed according to the rename dictionary.
    
    Example:
    --------
    import xarray as xr
    data = {'temp': ([], [0]), 'sali': ([], [1])}
    coords = {'time': [0]}
    ds = xr.Dataset(data, coords)
    renamed_ds = rename_vars_dims_coords(ds, {'temp': 'temperature', 'sali': 'salinity'})

    Dependencies:
    -------------
    xarray
    """
    if verbose: print('func: rename_vars_dims_coords')
    for old_name, new_name in rename_dict.items():
        if (old_name in ds.variables) | (old_name in ds.dims) | (old_name in ds.coords): 
            ds = ds.rename({old_name: new_name})
        #
    if verbose: print('endfunc')
    return ds
#

def split_coords_dimensions(ds, verbose=False):
    """
    Splits the latitude, longitude, and depth dimensions and coordinates of an xarray dataset into separate variables,
    updates their names, and assigns them back to the dataset.

    Parameters:
    -----------
    ds : xr.Dataset
        The xarray Dataset to be updated.
    verbose : bool, optional
        If True, prints the function name at the start and end of execution (default is False).
    
    Returns:
    --------
    xr.Dataset
        A new xarray Dataset with the latitude, longitude, and depth dimensions and coordinates split into separate variables
        and reassigned to the original dataset.
    
    Example:
    --------
    import xarray as xr
    data = {'temp': ([0, 1, 2], [0, 1]), 'sali': ([0, 1, 2], [0, 1])}
    coords = {'latitude': [0, 1, 2], 'longitude': [0, 1], 'depth': [0, 1, 2]}
    ds = xr.Dataset(data, coords)
    updated_ds = split_coords_dimensions(ds)

    Dependencies:
    -------------
    xarray
    """
    if verbose: print('func: split_coords_dimensions')
    new_coords = {}
    new_coords2 = {}
    new_dims = {}
    dim_name_dict = dict(latitude='j', longitude='i', depth='k')
    dimschanged = []
    for name, coord in ds.coords.items():
        if name in ds.dims and name in ["latitude", "longitude", "depth"]:
            new_coords[name + "_coord"] = coord
            new_dims[name] = dim_name_dict[name]
            new_coords2[name + "_coord"] = name
            dimschanged.append(name)
    if verbose: print('endfunc')
    for name in ['k', 'j', 'i']: 
        if name in ds.coords: dimschanged.append(name)
    #
    return ds.assign_coords(new_coords).rename_dims(new_dims).drop_vars(dimschanged).rename(new_coords2)
#

Others

In [6]:
def nan_helper(y):
    """Helper to handle indices and logical indices of NaNs.

    Input:
        - y, 1d numpy array with possible NaNs
    Output:
        - nans, logical indices of NaNs
        - index, a function, with signature indices= index(logical_indices),
          to convert logical indices of NaNs to 'equivalent' indices
    Example:
        >>> # linear interpolation of NaNs
        >>> nans, x= nan_helper(y)
        >>> y[nans]= np.interp(x(nans), x(~nans), y[~nans])
        nb: y[~nans] values of y that are not nans
            x(~nans) indexes of y that are not nans
    """

    return np.isnan(y), lambda z: z.nonzero()[0]
#

def get_esgf_dataset_filepaths(variable, sourceID, experimentID, 
                               freq='mon', grid='g*', version='latest', 
                               variant='r1i1p1f1',
                               mipera = 'CMIP6', diresgf='/mnt/reef-ns1002k-esgf/', verbose=False, **kwargs): 
    """
    Returns the filepaths of the remote netCDF files corresponding to the specified dataset of the Earth System
    Grid Federation (ESGF) data portal on NIRD.

    Parameters:
    -----------
    variable : str
        Variable to search for on ESGF data portal.
    sourceID : str
        Name of the data source on the ESGF data portal.
    experimentID : str
        Name of the experiment on the ESGF data portal.
    freq : str, optional
        Frequency of the data (default is 'mon').
    grid : str, optional
        Type of grid (default is 'g*').
    version : str, optional
        Version of the data being queried (default is 'latest').
    variant : str, optional
        Label for the variant of the data being queried (default is 'r1i1p1f1').
    mipera : str, optional
        Name of the CMIP era being queried (default is 'CMIP6').
    diresgf : str, optional
        Absolute path to the directory where the data is stored (default is '/mnt/reef-ns1002k-esgf/').
    verbose : bool, optional
        If True, prints the function name at the start and end of execution (default is False).
    **kwargs : dict, optional
        Other key-value arguments to be passed in the function.

    Returns:
    --------
    List[str]
        A list of filepaths corresponding to the specified dataset on the ESGF data portal.

    Example:
    --------
    fp_list = get_esgf_dataset_filepaths('tas', 'CanESM5', 'historical', freq='mon')

    Dependencies:
    -------------
    glob, sys
    """
    import glob, sys
    
    if verbose: print('func: get_esgf_dataset_filepaths')
    
    if experimentID in ['1pctCO2', 'piControl', 'historical', 'abrupt-4xCO2']: zwActivity='CMIP'
    elif experimentID in ['ssp126', 'ssp245', 'ssp585']: zwActivity='ScenarioMIP'
    else: sys.exit('Check experimentID, case not implemented')
    
    if sourceID in ['CESM2', 'CESM2-WACCM']: zwInstitutionID = 'NCAR'
    elif sourceID in ['ACCESS-ESM1-5']: zwInstitutionID = 'CSIRO'
    elif sourceID in ['CNRM-ESM2-1']: zwInstitutionID = 'CNRM-CERFACS'
    elif sourceID in ['CanESM5', 'CanESM5-CanOE']: zwInstitutionID = 'CCCma'
    elif sourceID in ['UKESM1-0-LL']: zwInstitutionID = 'MOHC'
    elif sourceID in ['GFDL-CM4', 'GFDL-ESM4']: zwInstitutionID = 'NOAA-GFDL'
    elif sourceID in ['IPSL-CM6A-LR', 'IPSL-CM6A-LR-INCA']: zwInstitutionID = 'IPSL'
    elif sourceID in ['MIROC-ES2L']: zwInstitutionID = 'MIROC'
    elif sourceID in ['MPI-ESM1-2-LR', 'ICON-ESM-LR']: zwInstitutionID = 'MPI-M'
    elif sourceID in ['NorESM2-LM']: zwInstitutionID = 'NCC'
    else: sys.exit('Check sourceID, case not implemented')
    
    ocean_list = ['fgco2', 'intpp', 'o2', 'thetao', 'so', 'agessc', 'po4', 'no3']
    if variable in ocean_list: zwTableID = 'O'+freq
    elif variable in ['areacello']: zwTableID='Ofx'
    elif variable in ['psl']: zwTableID='A'+freq
    else: sys.exit('!!! WARNING !!! Check variable, case not implemented')
        
    zwdname = diresgf + mipera +'/'+ zwActivity +'/'+ \
        zwInstitutionID +'/'+ sourceID +'/'+ \
        experimentID  +'/'+ variant +'/'+ zwTableID +'/'+ \
        variable+'/'+ grid +'/'+ version +'/'
    zwfname = variable +'_'+ zwTableID +'_'+ sourceID +'_'+ \
        experimentID +'_'+ variant +'_'+ grid +'*.nc' 

    if verbose: print('endfunc')
    return glob.glob(zwdname + zwfname)
#

Spatial operations

In [7]:
def expand_coords(zwda, coord_name):
    """
    Expand the specified coordinate dimension to match the shape of zwda.

    Args
    ----
    zwda : xarray.DataArray
        The DataArray to extract and expand the coordinate from.
    coord_name : str
        The name of the coordinate to be expanded.

    Returns
    -------
    xarray.DataArray
        The expanded coordinate dimension.

    Examples
    --------
    dep_expanded = expand_coords(my_dataarray, 'depth')
    """
    import numpy as np
    shape = zwda.shape
    zwcoord = zwda[coord_name]
    common_dims = tuple(set(zwcoord.dims) & set(zwda.dims)) # get the common dimensions between the coordinate and zwda
    diff_dims = tuple(set(zwcoord.dims) ^ set(zwda.dims)) # get the different dimensions between the coordinate and zwda
    axis_to_expand = zwda.get_axis_num(diff_dims)
    len_of_axis_to_expand = np.ones_like(shape)
    for aaa in axis_to_expand: len_of_axis_to_expand[aaa]=shape[aaa]
    len_of_axis_to_expand = tuple(len_of_axis_to_expand)
    zw2 = np.tile(np.expand_dims(zwda[coord_name], axis=axis_to_expand), len_of_axis_to_expand)
    output = xr.zeros_like(zwda)
    output.values = zw2
    output.attrs = zwda[coord_name].attrs
    output.name = coord_name
    return output
#

def regrid(zwds, method='bilinear', dlon=1, dlat=1, verbose=False): 
    """
    Regrids the input Dataset to a grid with the specified longitude and latitude intervals using conservative regridding.

    Parameters:
    -----------
    zwds : xr.Dataset
        Dataset containing the data to regrid. Note that the Dataset must have the `zwds.cf.get_bounds` attribute.
    method : str
        Regridding method. Options are
        - 'bilinear' (default, recommended)
        - 'conservative', **need grid corner information** (recommended)
        - 'conservative_normed', **need grid corner information**
        - 'patch'
        - 'nearest_s2d'
        - 'nearest_d2s'
    dlon : float (default=1)
        Longitude interval in degrees for the output grid.
    dlat : float (default=1)
        Latitude interval in degrees for the output grid.

    Returns:
    --------
    xr.Dataset
        Regridded Dataset.

    Example:
    --------
    # Regrid a Dataset to a 1x1 degree grid
    regridded_data = regrid(zwds, dlon=1, dlat=1)

    Dependencies:
    -------------
    xesmf
    """
    if verbose: print('func: regrid')
    import xesmf as xe
    
    ds_in  = zwds.rename({'longitude':'lon', 'latitude':'lat', 'j':'y', 'i':'x'})

    zlat = ds_in.lat.values
    zlon = ds_in.lon.values
    zlatb, zlonb = compute_horizontal_bounds(zlon=zlon, zlat=zlat)
    zlatbda = xr.DataArray(data=zlatb, 
                          name='lat_b', 
                          dims=['y_b', 'x_b'],
                          coords=dict(
                              lon_b=(["y_b", "x_b"], zlonb), 
                              lat_b=(["y_b", "x_b"], zlatb)
                          ), 
                          attrs=dict(description="grid latitudnal bounds",units="degN")
                         )
    zlonbda = xr.DataArray(data=zlonb, 
                          name='lon_b', 
                          dims=['y_b', 'x_b'],
                          coords=dict(
                              lon_b=(["y_b", "x_b"], zlonb), 
                              lat_b=(["y_b", "x_b"], zlatb)
                          ), 
                          attrs=dict(description="grid longitudinal bounds",units="degN")
                         )
    ds_in = ds_in.assign_coords({'lon_b':zlonbda, 'lat_b':zlatbda})

    ds_out = xe.util.grid_global(dlon, dlat) # Here I define the output grid, in that case I want a regular 1 by 1 degree
    regridder = xe.Regridder(ds_in, ds_out, method, ignore_degenerate=True, unmapped_to_nan=True) # this the line if I do not have the weight alread
    zwds_regrided = regridder(ds_in).rename({'lon':'longitude', 'lat':'latitude', 'x':'i', 'y':'j'})
    ds_in = ds_in.rename({'lon':'longitude', 'lat':'latitude', 'x':'i', 'y':'j'})
    for kkk in zwds_regrided.variables.keys(): zwds_regrided.variables[kkk].attrs = ds_in.variables[kkk].attrs
    return zwds_regrided
#

def compute_horizontal_bounds(zlon=None, zlat=None, verbose=False): 
    """
    Returns the bounds of the grid from 2 numpy arrays giving the latitude and longitude 
    of the point in the middle of the grid-cells.

    Parameters:
    -----------
    zlon : np.ndarray
        Array of longitudes.
    zlat : np.ndarray
        Array of latitudes.

    Returns:
    --------
    Tuple of two numpy arrays: latitude bounds and longitude bounds.
    
    Example:
    --------
    # Compute horizontal bounds for a grid
    lat_b, lon_b = compute_horizontal_bounds(zlon, zlat)

    Dependecies:
    ------------
    numpy
    """
    import numpy as np
    if verbose: print('func: compute_horizontal_bounds')
    if (zlon.any()==None) | (zlat.any()==None): 
        exit('WARNING! no zlon or no zlat')
    #   

    zlatb  = 0.5*( np.roll(zlat, 1, axis=0) + zlat)
    zlatb[ 0, :] = zlat[0, :] - (zlatb[1, :] - zlat[0, :])
    zlatb1 = zlat[-1, :] + (zlat[-1, :] - zlatb[-1, :])
    zlatb = np.concatenate([zlatb, zlatb1[np.newaxis, :]], axis=0)
    zlatb = np.concatenate([zlatb, zlatb[:, -1][:, np.newaxis]], axis=1)
    
    zlonb  = 0.5*( np.roll(zlon, 1, axis=1) + zlon)
    zlonb[:, 0] = zlon[:, 0] - (zlonb[:, 1] - zlon[:, 0])
    zlonb1 = zlon[:, -1] + (zlon[:, -1] - zlonb[:, -1])
    zlonb = np.concatenate([zlonb, zlonb1[:, np.newaxis]], axis=1)
    zlonb = np.concatenate([zlonb, zlonb[-1, :][np.newaxis, :]], axis=0)

    return zlatb, zlonb
#

def vertical_interpolation(zwda, newdep, dimensions='zyx', verbose=False): 
    from scipy.interpolate import interp1d
    if verbose: print('func: vertical_interpolation')
    
    depaxis = zwda.get_axis_num('k')

    zwdep = zwda.depth
    # fff = interp1d(zwdep, zwda, axis=0, assume_sorted=True, fill_value='extrapolate')
    fff = interp1d(zwdep, zwda, axis=depaxis, assume_sorted=True, bounds_error=False, fill_value=float('nan'))
    # fff = interp1d(zwdep, zwda, axis=1, assume_sorted=True, fill_value='extrapolate')
    aaa = fff(newdep)
    if dimensions=='zyx': 
        output = xr.DataArray(data=aaa, 
                              coords={'depth':newdep, 
                                      'latitude':zwda.latitude, 
                                      'longitude':zwda.longitude}, 
                              dims=('k', 'j', 'i'), 
                              attrs=zwda.attrs, 
                              name=zwda.name)
    elif dimensions=='zy': 
        output = xr.DataArray(data=aaa, 
                              coords={'latitude':zwda.latitude, 
                                      'depth':newdep}, 
                              dims=('k', 'j'), 
                              attrs=zwda.attrs, 
                              name=zwda.name)
    #
    return output
#

def compute_vertical_average(zwda, zmin=None, zmax=None, verbose=False):
    """
    Computes the vertical average of a 3D or 4D xarray DataArray over a specified depth range.

    Parameters:
    -----------
    zwda : xr.DataArray
        3D or 4D DataArray to average.
    zmin : float
        Minimum depth in meters to average over. (Default is None)
    zmax : float
        Maximum depth in meters to average over. (Default is None)
    verbose : bool
        Print verbose output if True.

    Returns:
    --------
    xr.DataArray
        2D or 3D DataArray containing the vertical average of the input DataArray over the specified depth range.

    Example:
    --------
    avg_data = compute_vertical_average(data, zmin=0, zmax=100)

    Dependencies:
    -------------
    scipy.interpolate.interp1d
    """
    if verbose: print('func: compute_vertical_average')
    from scipy.interpolate import interp1d


    depaxis = zwda.get_axis_num('k')

    zwdep = zwda.depth
    zwdepbnds = 0.5 * (zwdep + zwdep.shift(k=1, fill_value=0))
    zwdepbnds[0] = 0
    zwDdep = zwdepbnds.shift(k=-1) - zwdepbnds
    zwDdep[-1] = 2 * (zwdep[-1] - zwdepbnds[-1])

    omask = ~(zwda.isnull())
    bathy = (omask * zwDdep).sum(dim='k')
    omaskH = ~(zwda.isnull().all(dim='k'))
    
    if (zmin is None) & (zmax is None): 
        zw2 = (zwda*zwDdep).sum(axis=depaxis)
        output = xr.where(omaskH, zw2/bathy, np.nan)
    elif (not zmin is None) & (zmax is None): 
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        zw2 = (zwda*zwDdep).sum(axis=depaxis) - fff(zmin)
        zmin3D = zmin+bathy*0
        waterthick = xr.where(zmin3D <= bathy, bathy - zmin3D, np.nan)
        output = xr.where(omaskH, zw2/waterthick, np.nan)
    elif (zmin is None) & (not zmax is None):
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        zw2 = fff(zmax)
        zmax3D = zmax+bathy*0
        waterthick = xr.where(zmax3D <= bathy, zmax3D, bathy)
        output = xr.where(omaskH, zw2/waterthick, np.nan)
    else:       
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        zw2 = fff(zmax) - fff(zmin)
        zmax3D = zmax+bathy*0
        zmin3D = zmin+bathy*0
        waterthick = np.nan + bathy*0
        waterthick = xr.where((zmin3D<=bathy) & (zmax3D<=bathy), zmax3D - zmin3D, waterthick)
        waterthick = xr.where((zmin3D<=bathy) & (zmax3D>bathy) , bathy  - zmin3D, waterthick)
        output = xr.where(omaskH, zw2/waterthick, np.nan)
    #
    
    output.attrs=zwda.attrs
    return output
#

def compute_vertical_sum(zwda, zmin=None, zmax=None, verbose=False):
    """
    Computes the vertical average of a 3D or 4D xarray DataArray over a specified depth range.

    Parameters:
    -----------
    zwda : xr.DataArray
        3D or 4D DataArray to average.
    zmin : float
        Minimum depth in meters to sum over. (Default is None)
    zmax : float
        Maximum depth in meters to sum over. (Default is None)
    verbose : bool
        Print verbose output if True.

    Returns:
    --------
    xr.DataArray
        2D or 3D DataArray containing the vertical average of the input DataArray over the specified depth range.

    Example:
    --------
    avg_data = compute_vertical_average(data, zmin=0, zmax=100)

    Dependencies:
    -------------
    scipy.interpolate.interp1d
    """
    if verbose: print('func: compute_vertical_average')
    from scipy.interpolate import interp1d
    
    depaxis = zwda.get_axis_num('k')
    
    zwdep = zwda.depth
    zwdepbnds = 0.5*(zwdep + zwdep.shift(k=1, fill_value=0))
    zwdepbnds[0] = 0
    zwDdep = zwdepbnds.shift(k=-1) - zwdepbnds
    zwDdep[-1] = 2*(zwdep[-1] - zwdepbnds[-1])

    if 'year' in zwda.dims: mask = zwda.isel(year=0, drop=True).isnull().all(dim='k')
    else: mask = zwda.isnull().all(dim='k')

    if (zmin is None) & (zmax is None): 
        output = xr.where(mask==1, np.nan, (zwda*zwDdep).sum(axis=depaxis))
    elif (not zmin is None) & (zmax is None): 
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        output = xr.DataArray((zwda*zwDdep).sum(axis=depaxis) - fff(zmin), 
                              coords=zwda.isel(k=0, drop=True).coords,
                              dims=zwda.isel(k=0, drop=True).dims,
                              attrs=zwda.isel(k=0, drop=True).attrs)
        output = output.where(mask==0)
    elif (zmin is None) & (not zmax is None):
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        output = xr.DataArray(fff(zmax), 
                              coords=zwda.isel(k=0, drop=True).coords,
                              dims=zwda.isel(k=0, drop=True).dims,
                              attrs=zwda.isel(k=0, drop=True).attrs)
        output = output.where(mask==0)
    else:       
        zw = (zwda*zwDdep).cumsum(axis=depaxis)
        fff = interp1d(zwDdep.cumsum(), zw, axis=depaxis, bounds_error=False)
        output = xr.DataArray(fff(zmax) - fff(zmin), 
                              coords=zwda.isel(k=0, drop=True).coords,
                              dims=zwda.isel(k=0, drop=True).dims,
                              attrs=zwda.isel(k=0, drop=True).attrs)
        output = output.where(mask==0)
    #
    output.attrs=zwda.attrs
    return output
#

def zonal_avg(zwda, weights, longitudes=None, return_3Dselection=False, verbose=False): 
    """
    Compute weithed zonal average between lonmin and lonmax
    Inputs: 
        - zwda: xarray DataArray
        - weights: xarray DataArray used for weighting zonal average
        - lonmin, lonmax: floats defining longitude for avereging
        - return_3Dselection: boolean to return or not the data selected for averaging. Default is False
        - verbose: boolean controlling prints in the code
    Ouput: 
        xarray DataArray with zonal average. 
        If return_3Dselection=True, then data selected for zonal averagin are also returned
    """
    
    from scipy.interpolate import interp1d
    
    if verbose: print('func: zonal_avg')
    
    if 'k' in weights.dims: weight_for_zlat = weights.isel(k=0, drop=True)
    else: weight_for_zlat = weights
    
    if longitudes==None: 
        # compute zonal average weighted
        with xr.set_options(keep_attrs=True): 
            output = zwda.weighted(weights.fillna(0)).mean(dim='i', skipna=True)
            zwlat  = zwda['latitude'].weighted(weight_for_zlat.fillna(0)).mean(dim='i', skipna=True)
        #
    else: 
        # Select longitudes
        LONSEL = (zwda.longitude<=longitudes[1]) & (zwda.longitude>=longitudes[0])
        zw  = (zwda.T).where(LONSEL.T, np.nan).T
        zw2 = (zwda['latitude'].T).where(LONSEL.T, np.nan).T
        # compute zonal average weighted
        with xr.set_options(keep_attrs=True): 
            output = zw.weighted(weights.fillna(0)).mean(dim='i', skipna=True)
            zwlat  = zw2.weighted(weight_for_zlat.fillna(0)).mean(dim='i', skipna=True)
        #
    #
    
    #______________
    # Adjust latitude zonally average
    if zwlat.isnull().any(): # if there is nan values in zwlat, replace by the mean non weighted
        if longitudes==None: 
            zwlat = xr.where(zwlat.isnull(), 
                             zwda['latitude'].mean(dim='i', skipna=True),
                             zwlat)
        else: 
            zwlat = xr.where(zwlat.isnull(), 
                             xr.where( LONSEL.T, zwda['latitude'].T, np.nan).T.mean(dim='i', skipna=True), 
                             zwlat)
        #
    #
    if zwlat.isnull().any(): # if there is nan values in zwlat_iavg, replace by extrapolation
        aaa = zwlat.values
        t1 = np.arange(len(aaa))
        bbb = aaa[~np.isnan(aaa)]
        t2 = t1[~np.isnan(aaa)]
        f = interp1d(t2, bbb, fill_value='extrapolate')
        zwlat[np.isnan(aaa)] = f(t1[np.isnan(aaa)])
    #
        
    #______________
    # Set new latitude coordinates
    with xr.set_options(keep_attrs=True): output = output.assign_coords({'latitude':zwlat})
    if (return_3Dselection) & (longitudes!=None): 
        return output, zw
    else: return output
#

def gridArea(zlon=None, zlat=None, rd=6371000.00, verbose=False):
    """
    Compute grid_area from longitude and latitude
    Inputs: 
        - zlon: 2D numpy Array with longitude. If None (default) zlon 
        and zlat are 1deg grid
        - zlat: same as zlon but with latitude
        - rd: float with Earth radius: 6371000.00m (default)
    Output: xarray DataArray with grid cell area
    """
    # adapted from Jean Negrel: 
    # for n in range(len(lat)):
    #     for m in range(len(lon)):
    #         rlat1 = np.deg2rad(lat[n]+0.5)
    #         rlat2 = np.deg2rad(lat[n]-0.5)
    #         rlon1 = np.deg2rad(lon[m]+0.5)
    #         rlon2 = (np.deg2rad(lon[m]-0.5))
    #         drlon = rlon1 - rlon2
    #         if abs(drlon) >= (2*np.pi - abs(drlon)) :
    #             drlon = drlon+2*np.pi
    #         grid[n,m] = drlon*abs( np.sin(rlat2) - np.sin(rlat1) )
    # #print(grid.sum()-4*np.pi) # Must be as close as possible to 0
    # grid  = grid*rd*rd
    # grid = xr.DataArray(grid, coords=[lat, lon], dims=['lat', 'lon'])
    # #print(grid.sum()) # Actual Earth area: 510,065,623 km2 (source: Wikipedia)

    if verbose: print('func: gridArea')
    
    if (zlon.any()==None) | (zlat.any()==None): 
        zlon, zlat = np.meshgrid(np.arange(-179.5, 180, 1), np.arange(-89.5, 90, 1))
    #   
    
    zlat1        = 0.5*( np.roll(zlat, -1, axis=0) + zlat)
    zlat1[-1, :] = zlat[-1, :] + (zlat[-1, :] - zlat1[-2, :])
    zlat2        = 0.5*( np.roll(zlat, 1, axis=0) + zlat)
    zlat2[ 0, :] = zlat[0, :] - (zlat2[1, :] - zlat[0, :])

    zlon1        = 0.5*( np.roll(zlon, -1, axis=1) + zlon)
    zlon1[:, -1] = zlon[:, -1] + (zlon[:, -1] - zlon1[:, -2])
    zlon2        = 0.5*( np.roll(zlon, 1, axis=1) + zlon)
    zlon2[:,  0] = zlon[:, 0] - (zlon2[:, 1] - zlon[:, 0])

    zlat1_rad = np.deg2rad(zlat1)
    zlat2_rad = np.deg2rad(zlat2)
    zlon1_rad = np.deg2rad(zlon1)
    zlon2_rad = np.deg2rad(zlon2)
    
    dlon_rad = zlon1_rad - zlon2_rad
    dlon_rad = np.where( (abs(dlon_rad) >= (2*np.pi - abs(dlon_rad))), \
                        dlon_rad+2*np.pi, dlon_rad) 
    
    grid_area = rd*rd*dlon_rad*abs( np.sin(zlat2_rad) - np.sin(zlat1_rad) )
    output = xr.DataArray(data=grid_area, 
                          name='area', 
                          dims=['j', 'i'],
                          coords=dict(
                              longitude=(["j", "i"], zlon), 
                              latitude=(["j", "i"], zlat)
                          ), 
                          attrs=dict(description="Grid area",units="m2")
                         )
    if verbose: print(f"    Total grid area is {output.sum().values*1e-6:.1f}km2\
    \n    i.e. {output.sum()*1e-6/510065623*100:.1f}% of the actual Earth area (510065623km2 source: Wikipedia)\
    \n    Total grid area is {output.sum()*1e-6 - 510065623:.1f}km2 of the actual Earth area") 
    # if verbose: print("Total grid area is %.1f %% of actual Earth area: \
    # 510,065,623 km2 (source: Wikipedia)" %output.sum()*1e-6/510065623*100) 

    return output
#

def compute_vertical_bounds_thickness(zwdep1D): 
    """
    Compute vertical bounds and thicknesses 1D depth array.

    This function takes a one-dimensional series of depth values and computes
    the vertical boundaries and thicknesses for each layer.

    Parameters
    ----------
    zwdep1D : array-like
        One-dimensional array or series of depth values.

    Returns
    -------
    zwdepbnds1D : array-like
        One-dimensional array representing the vertical bounds for each layer.

    zwDdep1D : array-like
        One-dimensional array representing the thicknesses of each layer.

    Notes
    -----
    - The input is assumed to have a `shift` method, similar to a pandas Series.
    - The first vertical boundary is set explicitly to zero (surface level).
    - The thickness of the last layer is adjusted to ensure accurate representation.
    
    Example
    -------
    >>> import pandas as pd
    >>> depths = pd.Series([10, 20, 30, 40])
    >>> bounds, thicknesses = compute_vertical_bounds_(depths)
    >>> print(bounds)
    0     0.0
    1     5.0
    2    15.0
    3    25.0
    dtype: float64
    >>> print(thicknesses)
    0     5.0
    1    10.0
    2    10.0
    3    10.0
    dtype: float64
    """
    zwdepbnds1D = 0.5 * (zwdep1D + zwdep1D.shift(k=1, fill_value=0))
    zwdepbnds1D[0] = 0
    zwDdep1D = zwdepbnds1D.shift(k=-1) - zwdepbnds1D
    zwDdep1D[-1] = 2 * (zwdep1D[-1] - zwdepbnds1D[-1])
    return zwdepbnds1D, zwDdep1D
#    

Mapping functions

In [8]:
def adjust_vmin_vmax(data, q=0.1, round_up=True):
    """
    Computes vmin and vmax based on quantiles of the data and optionally
    rounds them up to values that make sense.
    
    Args:
    - data (ndarray): 2D array of data
    - q (float, optional): quantile used to compute vmin and vmax (default: 0.1)
    - round_up (bool, optional): whether to round up vmin and vmax to "nice" values (default: True)
    
    Returns:
    - tuple: vmin and vmax values
    """
    vmin = np.nanpercentile(data, q * 100)
    vmax = np.nanpercentile(data, (1 - q) * 100)
    
    if round_up:
        
        mag = 10 ** np.floor(np.log10(vmax - vmin))
        vmin1 = np.floor(vmin / mag)
        vmax1 = np.ceil(vmax / mag)
        nb_of_mag= np.round((vmax-vmin)/mag)
        icount = 0
        while (vmax1-vmin1)/nb_of_mag > 1.2: 
            dvmin = vmin-vmin1*mag
            dvmax = vmax1*mag - vmax
            if dvmax>dvmin: vmax1=np.round(vmax1-.1, 1)    
            else: vmin1=np.round(vmin1+.1, 1) 
        #
        vmax = vmax1*mag
        vmin = vmin1*mag
    #
    
    return vmin, vmax
#
def map_whole_globe(zwdataarray, zwax=None, bcolorbar=True, zwcbLabel='', zwcbExtend='both', verbose=False, **pcmkwargs): 
    """
    Plot a global map of the input DataArray 
    Inputs: 
        - zwax are GeoAxes created with cartopy
          e.g.:
            lonlatproj = ccrs.PlateCarree()
            ccrsproj = ccrs.Robinson(central_longitude=-155)
            fig = plt.figure(figsize = (10, 10))
            ax = plt.axes(projection=ccrsproj)
            pcmkwargs = dict(transform=lonlatproj)
            map_whole_globe(zwda, ax, **pcmkwargs)
          if None (default), theyr are created within the function
        - zwcbLabel: string to set label of colorbar. Default is ''
        - zwcbExtend: 'none', 'min', 'max' or 'both' (default)
        - **pcmkwargs are kwargs pass to matplotlib pcolormesh function
    Outputs: zwax the axes and  zwpcm the pcolormesh
    
    """
    import matplotlib.pyplot as plt
    from matplotlib import cm
    from matplotlib.colors import ListedColormap

    import cartopy.crs       as ccrs
    
    if verbose: print('func: map_whole_globe')
    
    zwlonlatproj = ccrs.PlateCarree()
    
    try:
        pcmkwargs
    except NameError:
        pcmkwargs_exists = False
    else:
        pcmkwargs_exists = True
    #

    if pcmkwargs_exists: 
        original_pcmkwargs = pcmkwargs.copy()
        if not 'vmin' in pcmkwargs: pcmkwargs['vmin'] = zwdataarray.quantile(.1).values
        elif pcmkwargs['vmin'] is None: pcmkwargs['vmin'] = zwdataarray.quantile(.1).values
        if not 'vmax' in pcmkwargs: pcmkwargs['vmax'] = zwdataarray.quantile(.9).values
        elif pcmkwargs['vmax'] is None: pcmkwargs['vmax'] = zwdataarray.quantile(.9).values
        if not 'transform' in pcmkwargs: pcmkwargs['transform'] = zwlonlatproj
        if not 'cmap' in pcmkwargs: 
            if (pcmkwargs['vmin']*pcmkwargs['vmax']<0): a = cm.get_cmap('RdBu',64)
            else: a = cm.get_cmap('viridis',64)
            if zwcbExtend=='both': 
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors[1:-1])
                newcmap.set_over(colors[-1])
                newcmap.set_under(colors[0])
            elif zwcbExtend=='max': 
                colors = a(np.linspace(0,1,10+1))
                newcmap = ListedColormap(colors[:-1])
                newcmap.set_over(colors[-1])
                newcmap.set_under("none")
            elif zwcbExtend=='min': 
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors[1:])
                newcmap.set_over("none")
                newcmap.set_under(colors[0])
            else:
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors)
                newcmap.set_over("none")
                newcmap.set_under(colors[0])
            #
            newcmap.set_bad("none")
            pcmkwargs['cmap'] = newcmap
        #
    else:
        pcmkwargs['vmin'] = zwdataarray.quantile(.1).values
        pcmkwargs['vmax'] = zwdataarray.quantile(.9).values
        pcmkwargs={'transform':zwlonlatproj}
        if (pcmkwargs['vmin']*pcmkwargs['vmax']<0): a = cm.get_cmap('RdBu',64)
        else: a = cm.get_cmap('viridis',64)
        if zwcbExtend=='both': 
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors[1:-1])
            newcmap.set_over(colors[-1])
            newcmap.set_under(colors[0])
        elif zwcbExtend=='max': 
            colors = a(np.linspace(0,1,10+1))
            newcmap = ListedColormap(colors[:-1])
            newcmap.set_over(colors[-1])
            newcmap.set_under("none")
        elif zwcbExtend=='min': 
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors[1:])
            newcmap.set_over("none")
            newcmap.set_under(colors[0])
        else:
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors)
            newcmap.set_over("none")
            newcmap.set_under(colors[0])
        #
        newcmap.set_bad("none")
        pcmkwargs['cmap'] = newcmap
    #

    XXX = zwdataarray['longitude'].values
    YYY = zwdataarray['latitude'].values
    ZZZ = zwdataarray.values
    if not zwax is None: 
        zwpcm = zwax.pcolormesh(XXX, YYY, ZZZ, **pcmkwargs)
    else: 
        print("WARNING! zwax is None, create zwfig and zwax")
        zwccrsproj = ccrs.Robinson(central_longitude=-155)
        zwfig = plt.figure(figsize = (10, 10))
        zwax = plt.axes(projection=zwccrsproj)
        zwpcm = zwax.pcolormesh(XXX, YYY, ZZZ, **pcmkwargs)
    #
    zwax.set_global()
    add_gridlines_coastline_and_land(zwax)
    # zwax.add_feature(cfeature.NaturalEarthFeature('physical', 'land', '110m',edgecolor='none',facecolor='wheat'))
    # zwax.add_feature(cfeature.COASTLINE)
    # zwax.gridlines(draw_labels=True, x_inline=False, y_inline=False,
    #                color='k', linestyle='dashed', linewidth=0.5)

    if bcolorbar: 
        zwcb = plt.colorbar(zwpcm, label=zwcbLabel, extend=zwcbExtend)
        zw = zwax.get_position()
        nx0 = zw.x1+.07*zw.width
        ny0 = zw.y0+.1*zw.height
        nhh = .8*zw.height
        nww = .1*nhh    
        zwcb.ax.set_position([nx0, ny0, nww, nhh])
        ttt=np.linspace(zwcb.vmin, zwcb.vmax, int(len(zwcb.cmap.colors)/2)+1)
        zwcb.ax.set_yticks(ttt)

    #___________
    # # restore the original kwargs before returning
    if pcmkwargs_exists:
        pcmkwargs.clear()
        pcmkwargs.update(original_pcmkwargs)
    #
    return zwax, zwpcm
#

def add_gridlines_coastline_and_land(zax, zxlocs=[-120, -60, 0, 60, 120, 180], \
                                     zylocs=[-60, -40, -20, 0, 20, 40, 60], \
                                     noticklabels=False, verbose=False): 

    import cartopy.feature as cfeature
    import matplotlib.ticker as mticker
    from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER, LatitudeFormatter, LongitudeFormatter
    
    if verbose: print('func: add_gridlines_coastline_and_land')
    
    # zax.add_feature(cfeature.NaturalEarthFeature('physical', 'land', '110m',edgecolor='none',facecolor='wheat'))
    zax.add_feature(cfeature.LAND, facecolor='wheat')
    zax.add_feature(cfeature.COASTLINE, linewidth=.5)
    
    if not noticklabels: gl = zax.gridlines(draw_labels=['left', 'right'], 
                                            x_inline=False, y_inline=False,
                                            xlocs = mticker.FixedLocator(zxlocs), 
                                            xlabel_style = {'size': 'small'}, 
                                            ylocs = mticker.FixedLocator(zylocs), 
                                            ylabel_style = {'size': 'small'}, dms=True,
                                            color='k', linestyle='dashed', linewidth=0.5, alpha=.5)
    else: gl = zax.gridlines(draw_labels=False, 
                             x_inline=False, y_inline=False,
                             xlocs = mticker.FixedLocator(zxlocs), 
                             ylocs = mticker.FixedLocator(zylocs), 
                             dms=True,
                             color='k', linestyle='dashed', linewidth=0.5, alpha=.5)
    # gl = zwax.gridlines(draw_labels=True, x_inline=False, y_inline=False,
    #                     color='k', linestyle='dashed', linewidth=0.5)
    return gl
#

def map_orthographic(zwdataarray, zoombox=(0, 0), \
                     zwax=None, zwcbLabel='', zwcbExtend='both', **pcmkwargs): 
    """
    Plot the input DataArray 
    zoombox either indicate the center [clon, clat] of the Orthographic projection (list of 2) 
        or the bounds of the projection [lonmin, lonmax, latmin, latmax]. Default is (0, 0)
    zwax are GeoAxes created with cartopy
    e.g.:
        lonlatproj = ccrs.PlateCarree()
        ccrsproj = ccrs.Orthographic(central_longitude=-20, central_latitude=-25)
        fig = plt.figure(figsize = (10, 10))
        ax = plt.axes(projection=ccrsproj)
        pcmkwargs = dict(transform=lonlatproj)
        map_whole_globe(zwda, ax, **pcmkwargs)
    if zwax = None (default), they are created within the function
    """
    print('func: map_orthographic')
    
    try:
        pcmkwargs
    except NameError:
        pcmkwargs_exists = False
    else:
        pcmkwargs_exists = True
    #


    zwlonlatproj = ccrs.PlateCarree()
    if pcmkwargs_exists: 
        if not 'transform' in pcmkwargs: pcmkwargs['transform'] = zwlonlatproj
    else: pcmkwargs={'transform':zwlonlatproj}

    XXX = zwdataarray['longitude'].values
    YYY = zwdataarray['latitude'].values
    ZZZ = zwdataarray.values
    if not zwax is None: 
        print("WARNING! zwax is given, ignoring zoombox")
        zwpcm = zwax.pcolormesh(XXX, YYY, ZZZ, **pcmkwargs)
        zwax.set_global()
        zxlocs = list(np.arange(-150, 200, 30))
        add_gridlines_coastline_and_land(zwax, zxlocs=zxlocs)

    else: 
        print("WARNING! zwax is None, create zwfig and zwax")

        if len(zoombox)==4: 
            lonmin, lonmax, latmin, latmax = zoombox
            clon, clat = lonmin+.5*(lonmax-lonmin), latmin+.5*(latmax-latmin)
        elif len(zoombox)==2: clon, clat= zoombox
        else: sys.exit('ERROR! Check zoombox, it should be a list of length 2 or 4')

        zwccrsproj = ccrs.Orthographic(central_longitude=clon, central_latitude=clat)
        zwfig = plt.figure(figsize = (5, 5))
        zwax = plt.axes(projection=zwccrsproj)
        zwpcm = zwax.pcolormesh(XXX, YYY, ZZZ, **pcmkwargs)
        

        if len(zoombox)==4 : 
            xmin, _ = zwax.projection.transform_point(lonmin, clat, zwlonlatproj)  
            xmax, _ = zwax.projection.transform_point(lonmax, clat, zwlonlatproj)  
            _, ymin = zwax.projection.transform_point(clon, latmin, zwlonlatproj)  
            _, ymax = zwax.projection.transform_point(clon, latmax, zwlonlatproj) 
            zwax.set_extent([xmin,xmax,ymin,ymax], crs=zwax.projection)
            zxlocs = list(np.arange(lonmin, lonmax, np.round((50+60)/4, -1)))
            add_gridlines_coastline_and_land(zwax, zxlocs=zxlocs)
        elif len(zoombox)==2: 
            zwax.set_global()
            zxlocs = list(np.arange(-150, 200, 30))
            add_gridlines_coastline_and_land(zwax, zxlocs=zxlocs)
        #
    #

    zwcb = plt.colorbar(zwpcm, label=zwcbLabel, extend=zwcbExtend)
    zw = zwax.get_position()
    nx0 = zw.x1+.15*zw.width
    ny0 = zw.y0+.1*zw.height
    nhh = .8*zw.height
    nww = .1*nhh    
    zwcb.ax.set_position([nx0, ny0, nww, nhh])
    ttt=np.linspace(zwcb.vmin, zwcb.vmax, int(len(zwcb.cmap.colors)/2)+1)
    zwcb.ax.set_yticks(ttt)
    return zwax, zwpcm
#


def plot_section(dataarray, ax=None, bcolorbar=True, cbLabel='', cbExtend='both', title='Zonal average', plottype='pcolormesh', 
                 xlim=(-90, 90), xlabel='Deg. North', ylim=(5000, -10), ylabel='Depth [m]', inline=False, 
                 kwpcm={}, kwct={}, verbose=False):
    
    """
    Plot a vertical & meridional section
    Inputs: 
        - dataarray: 2D xarray DataArray to plot
        - ax: a set of matplolib axes (e.g. plt.axes), if None (default) axes are created
        - cbLabel: a string for labelling the colorbar. Default is ''
        - cbExtend: 'none', 'min', 'max' or 'both' (default)
        - title: string to give title to the plot. Default is 'Zonal average'
        - plottype: 'pcolormesh' (default) or 'contour' or 'both'. To define the type of plot.
        - xlim: tuple defining the x axis limits. Default is (-90, 90)
        - ylim: tuple defining the y axis limits. Default is (1000, -10)
        - xlabel: string for x axis label
        - ylabel: string for y axis label
        - inline: boolean to set contour labels. Default is False
        - kwpcm: kwargs to pass to pcolormesh
        - kwct: kwargs to pass to contour
    """
    
    #___________
    # Import
    import matplotlib.pyplot as plt
    from matplotlib import cm
    from matplotlib.colors import ListedColormap
    
    if verbose: print('func: plot_section')
    
    #___________
    # Do kwpcm exist?
    try:
        kwpcm
    except NameError:
        kwpcm_exists = False
    else:
        kwpcm_exists = True
    #

    #___________
    # set cmap, vmin and vmax if they are not define
    if kwpcm_exists: 
        original_kwpcm = kwpcm.copy()
        if not 'cmap' in kwpcm: 
            a = cm.get_cmap('viridis',64)
            if cbExtend=='both': 
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors[1:-1])
                newcmap.set_over(colors[-1])
                newcmap.set_under(colors[0])
            elif cbExtend=='max': 
                colors = a(np.linspace(0,1,10+1))
                newcmap = ListedColormap(colors[:-1])
                newcmap.set_over(colors[-1])
                newcmap.set_under("none")
            elif cbExtend=='min': 
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors[1:])
                newcmap.set_over("none")
                newcmap.set_under(colors[0])
            else:
                colors = a(np.linspace(0,1,10+2))
                newcmap = ListedColormap(colors)
                newcmap.set_over("none")
                newcmap.set_under(colors[0])
            #
            newcmap.set_bad("none")
            kwpcm['cmap'] = newcmap
        #
        if not 'vmin' in kwpcm: kwpcm['vmin'] = dataarray.quantile(.1).values
        if not 'vmax' in kwpcm: kwpcm['vmax'] = dataarray.quantile(.9).values
    elif (not kwpcm_exist) & (plottype in ['pcolormesh', 'both']):
        kwpcm = {}
        a = cm.get_cmap('viridis',64)
        if cbExtend=='both': 
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors[1:-1])
            newcmap.set_over(colors[-1])
            newcmap.set_under(colors[0])
        elif cbExtend=='max': 
            colors = a(np.linspace(0,1,10+1))
            newcmap = ListedColormap(colors[:-1])
            newcmap.set_over(colors[-1])
            newcmap.set_under("none")
        elif cbExtend=='min': 
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors[1:])
            newcmap.set_over("none")
            newcmap.set_under(colors[0])
        else:
            colors = a(np.linspace(0,1,10+2))
            newcmap = ListedColormap(colors)
            newcmap.set_over("none")
            newcmap.set_under(colors[0])
        #
        newcmap.set_bad("none")
        kwpcm['cmap'] = newcmap
        kwpcm['vmin'] = dataarray.quantile(.1).values
        kwpcm['vmax'] = dataarray.quantile(.9).values
    #

    #___________
    # Do kwct exist?
    try:
        kwct
    except NameError:
        kwct_exists = False
    else:
        kwct_exists = True
    #

    #___________
    # set colors for contour
    if kwct_exists: 
        original_kwct = kwct.copy()
        if not 'colors' in kwct: kwct['colors'] = 'red'
    elif (not kwct_exists) & (plottype in ['contour', 'both']):
        kwct = {'colors':'red'}
    #

    #___________
    # Create axes if needed
    if ax is None: 
        if verbose: print("WARNING! ax is None, create ax")
        ax = plt.axes()
    #
    
    #___________
    # Plot
    if plottype in ['pcolormesh', 'both']: zwpcm = ax.pcolormesh(dataarray.latitude, dataarray.depth, dataarray, **kwpcm)
    if plottype in ['contour', 'both']: 
        zwct = ax.contour(dataarray.latitude, dataarray.depth, dataarray, **kwct)
        ax.clabel(zwct, inline=inline)
    #
    #___________
    # Decorate plot
    ax.set_ylim(ylim)
    ax.set_ylabel(ylabel)
    ax.set_xlim(xlim)
    ax.set_xlabel(xlabel)
    ax.set_title(title)
    
    
    #___________
    # Colorbar
    if bcolorbar: 
        try: 
            zwcb = plt.colorbar(zwpcm, label=cbLabel, extend=cbExtend)
            zw = ax.get_position()
            nx0 = zw.x1+.07*zw.width
            ny0 = zw.y0+.1*zw.height
            nhh = .8*zw.height
            nww = .1*nhh    
            zwcb.ax.set_position([nx0, ny0, nww, nhh])
            ttt=np.linspace(zwcb.vmin, zwcb.vmax, int(len(zwcb.cmap.colors)/2)+1)
            zwcb.ax.set_yticks(ttt)
        except: 
            if verbose: print('No colorbar')
        #
    #

    #___________
    # # restore the original kwargs before returning
    if kwpcm_exists:
        kwpcm.clear()
        kwpcm.update(original_kwpcm)
    #
    if kwct_exists:
        kwct.clear()
        kwct.update(original_kwct)
    #

    #___________
    # Return
    if ('zwpcm' in locals()) & ('zwct' in locals()): 
        return zwpcm, zwct
    elif ('zwpcm' in locals()): 
        return zwpcm
    elif ('zwct' in locals()): 
        return zwct
    else: 
        print('Nothing have been plot')
        return 
#

Diagnostics

In [9]:
def compute_potential_density_anomaly( temp, sali ): 
    """
    Computes the potential density anomaly with reference pressure of 0 dbar given temperature and salinity arrays.

    Parameters:
    -----------
    temp : xarray.DataArray
        Array of temperature values. 
        Should have the following dimensions: latitude, longitude, and depth.
    sali : xarray.DataArray
        Array of salinity values.
        Should have the following dimensions: latitude, longitude, and depth.

    Returns:
    --------
    xarray.DataArray
        Array of potential density anomaly values with reference pressure of 0 dbar. 
        Has the same dimensions as `temp` and `sali`.
    
    Dependencies:
    -------------
    gsw_xarray
    """
    import gsw_xarray as gsw

    zwlat = temp['latitude']
    zwlon = temp['longitude']
    zwlev = temp['depth']

    # Compute sea pressure
    zwseapr = gsw.conversions.p_from_z(-zwlev, zwlat)

    # Compute absolute salinity
    zwSA = gsw.conversions.SA_from_SP(sali, zwseapr, zwlon, zwlat)

    # Compute conservative temperature
    zwCT = gsw.conversions.CT_from_pt(zwSA, temp)

    # Compute potential density anomaly
    zwsigma0 = gsw.density.sigma0(zwSA, zwCT)

    zwsigma0.attrs['comment']= 'Potential density anomaly with reference pressure of 0 dbar, this being this particular \
    potential density minus 1000 kg/m^3. This function has inputs of Absolute Salinity and Conservative Temperature. \
    This function uses the computationally-efficient expression for specific volume in terms of SA, CT and p (Roquet et al., 2015).'
    zwsigma0.attrs['long_name']='Potential density anomaly with reference pressure of 0 dbar'
    addtxt = str(datetime.datetime.now())+' '+'gsw.density.sigma0 to compute potential density anomaly with reference pressure of 0 dbar'
    try : zwsigma0.attrs['history'] = addtxt+' ; '+zwsigma0.attrs['history']
    except: zwsigma0.attrs['history']=addtxt
    
    return zwsigma0
#

def coloring_2D_bins(xxx, yyy, ccc, stat='mean', nbin=100, delta_bin=None): 
    """
    Compute the binned statistical summary of coloring values for 2D data arrays.

    This function flattens the 2D input arrays, removes nan values, and computes the
    specified statistical summary (by default, the mean) for the given bins in the
    two-dimensional space defined by `xxx` and `yyy`.

    Parameters
    ----------
    xxx : array-like
        2D array representing the x-coordinates of the data points.
    yyy : array-like
        2D array representing the y-coordinates of the data points.
    ccc : array-like
        2D array representing the coloring values corresponding to the data points.
    stat : str, optional
        The statistic to compute (default is 'mean'). Possible values are 'mean',
        'median', 'count', 'sum', 'std', etc., as supported by scipy.stats.binned_statistic_dd.
    nbin : int, optional
        Number of bins along each dimension (default is 100) if `delta_bin` is not provided.
    delta_bin : array-like, optional
        Array of length 2 specifying the bin width for each dimension. If provided,
        `nbin` is ignored, and the bins are computed based on the specified widths.

    Returns
    -------
    dict
        A dictionary with the following keys:
            'xxx': array
                The edges of the bins along the x-dimension.
            'yyy': array
                The edges of the bins along the y-dimension.
            'ccc': array
                The computed statistical summary for each bin, after transposing the result.

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.stats import binned_statistic_dd
    >>> xxx = np.random.random((100, 100))
    >>> yyy = np.random.random((100, 100))
    >>> ccc = np.random.random((100, 100))
    >>> result = coloring_2D_bins(xxx, yyy, ccc, stat='mean', nbin=50)
    >>> print(result['xxx'])
    >>> print(result['yyy'])
    >>> print(result['ccc'])
    """
    # Flatten the array to 1D
    xxx_flat = xxx.flatten()
    yyy_flat = yyy.flatten()
    ccc_flat = ccc.flatten()
    # Remove nans
    nans_xxx, x = nan_helper(xxx_flat)
    nans_yyy, x = nan_helper(yyy_flat)
    nans_ccc, x = nan_helper(ccc_flat)
    no_nans = (~nans_xxx) & (~nans_yyy) & (~nans_ccc)
    zwxxx = xxx_flat[no_nans]
    zwyyy = yyy_flat[no_nans]
    zwccc = ccc_flat[no_nans]
    # Combine xxx_flat and yyy_flat for binning
    sample = np.vstack((zwxxx, zwyyy)).T
    # Compute the average of coloring for each bin
    if (delta_bin==None): 
        num_bins = [nbin, nbin]  # Number of bins for each dimension, adjust as required
        stat_result, edges, bin_number = stats.binned_statistic_dd(sample, values=zwccc, statistic=stat, bins=num_bins)
    else: 
        sample_min = np.min(sample, axis=0)
        sample_max = np.max(sample, axis=0)
        num_bins = [np.arange(sample_min[dim], sample_max[dim]+.5*delta_bin[dim], delta_bin[dim]) for dim in range(sample.shape[1])]
        stat_result, edges, bin_number = stats.binned_statistic_dd(sample, values=zwccc, statistic=stat, bins=num_bins)
    #
    return {'xxx':edges[0], 'yyy':edges[1], 'ccc':stat_result.T}
#

def weighted_quantile(values, quantiles, sample_weight=None, values_sorted=False, old_style=False):
    """
    Compute weighted quantiles, handling NaN values.

    Parameters:
    values (array-like): The data values.
    quantiles (array-like): The desired quantiles in [0, 1].
    sample_weight (array-like): The weights for each value. If None, each value is assumed to have equal weight.
    values_sorted (bool): If True, assumes `values` are sorted.
    old_style (bool): If True, use the old-style quantile definition used in numpy <= 1.4.0.

    Returns:
    numpy.ndarray: The computed weighted quantiles.
    """
    
    values = np.array(values)
    quantiles = np.array(quantiles)
    
    if sample_weight is None:
        sample_weight = np.ones_like(values)
    else:
        sample_weight = np.array(sample_weight)
    
    # Remove NaN entries
    valid_mask = ~np.isnan(values) & ~np.isnan(sample_weight)
    values = values[valid_mask]
    sample_weight = sample_weight[valid_mask]

    if not values_sorted:
        sorter = np.argsort(values)
        values = values[sorter]
        sample_weight = sample_weight[sorter]

    weighted_quantiles = np.cumsum(sample_weight) - 0.5 * sample_weight
    weighted_quantiles /= np.sum(sample_weight)

    if old_style:
        return np.interp(quantiles, weighted_quantiles, values)
    else:
        return np.interp(quantiles, weighted_quantiles, values, left=values[0], right=values[-1])
#

AOU timeseries and validation and consistency

Prepare data2plot

AOU timeseries

In [ ]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# AOU timeseries and validation and consistency')
print('## Prepare data2plot')
print('### AOUO timeseries')

# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM',
#             'CanESM5', 'CNRM-ESM2-1', 'GFDL-ESM4', 'UKESM1-0-LL']
esm_list = ['NorESM2-LM', 'CanESM5', 'CNRM-ESM2-1', 'GFDL-ESM4', 'UKESM1-0-LL']
var='aou'

for iesm, vesm in enumerate(esm_list): 
    
    print(f'For {vesm}...')
    
    data2plot = {}

    #________________
    # Get data
    print('    Get data...')

    simu = 'historical'
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    fname_list = glob.glob(fname)
    simu = 'ssp585'
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    fname_list.extend(glob.glob(fname))
    
    zwda = xr.open_mfdataset(fname_list, **kwopenmfds)[var] # in mol.m-3
    
    # simu = 'historical'
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_185[0-9].nc"
    # fname_list = glob.glob(fname)
    # zwda = xr.open_mfdataset(fname_list, **kwopenmfds)[var] # in mol.m-3

    #________________
    # Get drift
    print('    Get drift...')
    
    tperiod_drift = f'{picontrol_refyear_dict[vesm]:04}-{picontrol_refyear_dict[vesm]+(2099-1850):04}'
    fname = dirshared+vesm+'_piControl_'+var+'_linregress_on_'+tperiod_drift+'.nc'
     
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
     
    drift_slope  = zwds['slope'] # mol.m-3.yr-1
    drift_itcpt  = zwds['intercept'] 
    
    xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(2099-1850)+.5, 1, dtype='int')
    xxx_year_adjusted = np.arange(1850, 2099.5, 1, dtype='int')
    year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    drift = year * drift_slope + drift_itcpt
    
    # xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(1859-1850)+.5, 1, dtype='int')
    # xxx_year_adjusted = np.arange(1850, 1859.5, 1, dtype='int')
    # year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    # drift = year * drift_slope + drift_itcpt

    del zwds, drift_slope, drift_itcpt, xxx, xxx_year_adjusted, year
    
    #________________
    # Drift correction
    print('    Drift correction...')
    zwda_nodrift = zwda - drift
    
    #________________
    # FOR MPI model maybe
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda = zwda.isel(j=slice(None, None, -1))
        zwda_nodrift = zwda_nodrift.isel(j=slice(None, None, -1))
    #

    #_________________
    # Compute volume
    print('    Compute volume...')
    
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda.depth)
    
    omask = ~(zwda.isel(year=0, drop=True).isnull())
    volume = omask* depthickness * zarea 

    del depbounds, depthickness
    
    #_______________
    # Load
    # print('    Load...')
    # zwda.load()
    # zwda_nodrift.load()
    # zarea.load()
    # volume.load()
    
    data2plot['volume'] = {}
    data2plot['raw'] = {}
    data2plot['nodrift'] = {}
    
    #_______________
    # Total water column
    print('    Total water column...')
    data2plot['volume']['total'] = volume.sum(dim=('k', 'j', 'i'), skipna=True).values
    zw = (zwda*volume).sum(dim=('k', 'j', 'i'), skipna=True)
    data2plot['raw']['total'] = {'X': zw.year.values, 'Y': zw.values}
    zw = (zwda_nodrift*volume).sum(dim=('k', 'j', 'i'), skipna=True)
    data2plot['nodrift']['total'] = {'X': zw.year.values, 'Y': zw.values}
    
    #_______________
    # Above and below ???? meters
    # for zlim in [1000, 1500, 2000]: 
    for zlim in [1000]: 

        # above
        print(f'    Above {zlim} meters...')
        zw = (compute_vertical_sum(volume*0+1, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['volume']['a'+str(zlim)] = zw.values
        zw = (compute_vertical_sum(zwda, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['raw']['a'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        zw = (compute_vertical_sum(zwda_nodrift, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['nodrift']['a'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        del zw
        gc.collect()
        
        # below
        print(f'    Below {zlim} meters...')
        zw = (compute_vertical_sum(volume*0+1, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['volume']['b'+str(zlim)] = zw.values
        zw = (compute_vertical_sum(zwda, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['raw']['b'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        zw = (compute_vertical_sum(zwda_nodrift, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['nodrift']['b'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        
    #

    #_______________
    # Save data2plot in pickle
    print('    Save data2plot in pickle...')
    savefile = dirout + 'data2plot_aou_timeseries_'+vesm+'.pckl'
    with open(savefile, "wb") as file:
        pickle.dump(data2plot, file)
    #
    print('    File saved: '+savefile)
    
    del zw, data2plot, zwda, zwda_nodrift, zarea, volume
    gc.collect()
#
2024-12-17 11:41:19.063610
# AOU timeseries and validation and consistency
## Prepare data2plot
### AOUO timeseries
For MPI-ESM1-2-LR...
    Get data...
    Get drift...
    Drift correction...
    Compute volume...
    Total water column...
    Above 1000 meters...
    Below 1000 meters...
    Save data2plot in pickle...
    File saved: 24-12-17-figures-for-storyline-v1/data2plot_aou_timeseries_MPI-ESM1-2-LR.pckl
For ACCESS-ESM1-5...
    Get data...
    Get drift...
    Drift correction...
    Compute volume...
    Total water column...
[########################################] | 100% Completed | 13.13 s
    Above 1000 meters...
[########################################] | 100% Completed | 10.34 s
[########################################] | 100% Completed | 11.61 s
[########################################] | 100% Completed | 15.15 s
[########################################] | 100% Completed | 15.09 s
    Below 1000 meters...
[########################################] | 100% Completed | 12.64 s
[########################################] | 100% Completed | 10.86 s
[########################################] | 100% Completed | 14.90 s
[########################################] | 100% Completed | 15.12 s
[########################################] | 100% Completed | 13.25 s
    Save data2plot in pickle...
    File saved: 24-12-17-figures-for-storyline-v1/data2plot_aou_timeseries_ACCESS-ESM1-5.pckl
For IPSL-CM6A-LR...
    Get data...
    Get drift...
    Drift correction...
[########################################] | 100% Completed | 24.21 s
[########################################] | 100% Completed | 18.44 s
[########################################] | 100% Completed | 19.03 s
    Compute volume...
[########################################] | 100% Completed | 14.56 s
[########################################] | 100% Completed | 10.98 s
    Total water column...
[########################################] | 100% Completed | 13.53 s
[########################################] | 100% Completed | 31.66 s
[########################################] | 100% Completed | 27.82 s
    Above 1000 meters...
[########################################] | 100% Completed | 25.12 s

For comparison with worl ocean atlas

Temporal mean and regriding

In [21]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# AOU timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For comparison with world ocean atlas')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'historical'
var='aou'

tperiod = slice('1971', '2000')


#--------------------
# Get WOA datas
#--------------------
print('Get WOA datas...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
# zwfname = 'woa23_all_A00_01.nc'
zwfname = 'woa23_decav71A0_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']

for iesm, vesm in enumerate(esm_list): 
    
    print(f'Prepare data for {vesm}...')
    
    #________________
    # Get data
    print('   Get data...')
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds)
    zwda1 = zwds1[var] # in mol.m-3
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda1 = zwda1.isel(j=slice(None, None, -1))
    #

    #_________________
    # Temporal average
    print('    Temporal average...')
    zwda1 = zwda1.sel(year=tperiod).mean(dim='year').compute()

    #_________________
    # Regrid
    print('    Regrid...')
    zwds = zwda1.to_dataset()
    zwda1_rgd_tmp = regrid(zwds, method='bilinear')[var]
    zwda1_rgd = vertical_interpolation(zwda1_rgd_tmp, zwda_woa.depth)

    #_________________
    # save in netcdf
    print('    Save in netcdf...')
    zwds2 = zwda1.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
    zwds2 = zwda1_rgd.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
#
print('Done')
2025-05-05 16:11:28.328294
# AOU timeseries and validation and consistency
## Prepare data2plot
### For comparison with world ocean atlas
Get WOA datas...
Prepare data for MPI-ESM1-2-LR...
   Get data...
    Temporal average...
    Regrid...
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_aou_1971-2000-mean_regrided.nc
Prepare data for ACCESS-ESM1-5...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_aou_1971-2000-mean_regrided.nc
Prepare data for IPSL-CM6A-LR...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_aou_1971-2000-mean_regrided.nc
Prepare data for MIROC-ES2L...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_aou_1971-2000-mean_regrided.nc
Prepare data for NorESM2-LM...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_aou_1971-2000-mean_regrided.nc
Done
peak memory: 2734.81 MiB, increment: 2421.32 MiB
CPU times: user 53.1 s, sys: 16 s, total: 1min 9s
Wall time: 5min 19s

For consistency in ∆AOU

Temporal mean and regriding

In [22]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# AOU timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For consistency in ∆AOU')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'ssp585'
var='aou'

tperiod = slice('2070', '2099')

# ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

#--------------------
# Get WOA datas
#--------------------
print('Get WOA datas...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
# zwfname = 'woa23_all_A00_01.nc'
zwfname = 'woa23_decav71A0_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']

for iesm, vesm in enumerate(esm_list): 
    
    print(f'Prepare data for {vesm}...')
    
    #________________
    # Get data
    print('   Get data...')
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds)
    zwda1 = zwds1[var] # in mol.m-3
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda1 = zwda1.isel(j=slice(None, None, -1))
    #

    #_________________
    # Temporal average
    print('    Temporal average...')
    zwda1 = zwda1.sel(year=tperiod).mean(dim='year').compute()

    #_________________
    # Regrid
    print('    Regrid...')
    zwds = zwda1.to_dataset()
    zwda1_rgd_tmp = regrid(zwds, method='bilinear')[var]
    zwda1_rgd = vertical_interpolation(zwda1_rgd_tmp, zwda_woa.depth)

    #_________________
    # save in netcdf
    print('    Save in netcdf...')
    zwds2 = zwda1.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
    zwds2 = zwda1_rgd.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
#
print('Done')
2025-05-05 16:18:06.109607
# AOU timeseries and validation and consistency
## Prepare data2plot
### For consistency in ∆AOU
Get WOA datas...
Prepare data for MPI-ESM1-2-LR...
   Get data...
    Temporal average...
    Regrid...
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_ssp585_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_ssp585_aou_2070-2099-mean_regrided.nc
Prepare data for ACCESS-ESM1-5...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_ssp585_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_ssp585_aou_2070-2099-mean_regrided.nc
Prepare data for IPSL-CM6A-LR...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_ssp585_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_ssp585_aou_2070-2099-mean_regrided.nc
Prepare data for MIROC-ES2L...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_ssp585_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_ssp585_aou_2070-2099-mean_regrided.nc
Prepare data for NorESM2-LM...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_ssp585_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_ssp585_aou_2070-2099-mean_regrided.nc
Done
peak memory: 3712.79 MiB, increment: 2209.99 MiB
CPU times: user 33.4 s, sys: 11 s, total: 44.5 s
Wall time: 2min 53s

For removing piControl drift in ∆AOU

Temporal mean and regriding

In [23]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# AOU timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For removing piControl drift in ∆AOU')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
var='aou'

# ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

#--------------------
# Get WOA datas
#--------------------
print('Get WOA datas...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
# zwfname = 'woa23_all_A00_01.nc'
zwfname = 'woa23_decav71A0_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']

for iesm, vesm in enumerate(esm_list): 
    
    print(f'Prepare data for {vesm}...')

    
    #________________
    # Get drift
    print('    Get drift...')
    
    tperiod_drift = f'{picontrol_refyear_dict[vesm]:04}-{picontrol_refyear_dict[vesm]+(2099-1850):04}'
    fname = dirshared+vesm+'_piControl_'+var+'_linregress_on_'+tperiod_drift+'.nc'
     
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
     
    drift_slope  = zwds['slope'] # mol.m-3.yr-1
    drift_itcpt  = zwds['intercept'] 
    
    xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(2099-1850)+.5, 1, dtype='int')
    xxx_year_adjusted = np.arange(1850, 2099.5, 1, dtype='int')
    year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    zwda = year * drift_slope + drift_itcpt
    zwda.name = var

    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda = zwda.isel(j=slice(None, None, -1))
    #

    #_________________
    # Temporal average
    print('    Temporal average...')
    tperiod1 = slice('1971', '2000')
    tperiod2 = slice('2070', '2099')
    zwda1 = zwda.sel(year=tperiod1).mean(dim='year').compute()
    zwda2 = zwda.sel(year=tperiod2).mean(dim='year').compute()

    #_________________
    # Regrid
    print('    Regrid...')
    
    zwds1 = zwda1.to_dataset()
    zwda1_rgd_tmp = regrid(zwds1, method='bilinear')[var]
    zwda1_rgd = vertical_interpolation(zwda1_rgd_tmp, zwda_woa.depth)

    zwds2 = zwda2.to_dataset()
    zwda2_rgd_tmp = regrid(zwds2, method='bilinear')[var]
    zwda2_rgd = vertical_interpolation(zwda2_rgd_tmp, zwda_woa.depth)

    #_________________
    # save in netcdf
    print('    Save in netcdf...')

    zwds_tosave = zwda1.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod1.start)
    ymax = '%04d'%int(tperiod1.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)
    
    zwds_tosave = zwda1_rgd.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod1.start)
    ymax = '%04d'%int(tperiod1.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

    zwds_tosave = zwda2.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod2.start)
    ymax = '%04d'%int(tperiod2.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

    zwds_tosave = zwda2_rgd.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod2.start)
    ymax = '%04d'%int(tperiod2.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

#
print('Done')
2025-05-05 16:21:48.013944
# AOU timeseries and validation and consistency
## Prepare data2plot
### For removing piControl drift in ∆AOU
Get WOA datas...
Prepare data for MPI-ESM1-2-LR...
    Get drift...
    Temporal average...
    Regrid...
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_aou_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_aou_2070-2099-mean_regrided.nc
Prepare data for ACCESS-ESM1-5...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 10.65 s
[########################################] | 100% Completed | 10.75 s
[########################################] | 100% Completed | 10.42 s
[########################################] | 100% Completed | 10.52 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_aou_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_aou_2070-2099-mean_regrided.nc
Prepare data for IPSL-CM6A-LR...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 17.27 s
[########################################] | 100% Completed | 17.37 s
[########################################] | 100% Completed | 17.51 s
[########################################] | 100% Completed | 17.61 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_aou_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_aou_2070-2099-mean_regrided.nc
Prepare data for MIROC-ES2L...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 11.23 s
[########################################] | 100% Completed | 11.33 s
[########################################] | 100% Completed | 11.03 s
[########################################] | 100% Completed | 11.13 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_aou_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_aou_2070-2099-mean_regrided.nc
Prepare data for NorESM2-LM...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 18.61 s
[########################################] | 100% Completed | 18.71 s
[########################################] | 100% Completed | 18.71 s
[########################################] | 100% Completed | 18.81 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_aou_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_aou_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_aou_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_aou_2070-2099-mean_regrided.nc
Done
peak memory: 40132.42 MiB, increment: 38538.69 MiB
CPU times: user 1min 33s, sys: 56.2 s, total: 2min 30s
Wall time: 2min 34s

Prepare data2plot

In [24]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# AOU timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For validation and consistency')


esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
var='aou'
zfact_esm = 1000.
zfact_obs = 1/1.025 # umol.kg-1 to umol.l-1 = mmol.m-3
units = 'mmol.m$^{-3}$'

ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

data2plot = {'units':units, 'ocean_dict':ocean_dict}

#--------------------
# Prepare WOA datas
#--------------------
print('Prepare WOA datas...')

#________________
# Get data
print('    Get data...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
# zwfname = 'woa23_all_A00_01.nc'
zwfname = 'woa23_decav71A0_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']*zfact_obs

zarea_woa = gridArea(zlon=zwda_woa.longitude.values, zlat=zwda_woa.latitude.values)

#________________
# Zonal average on ocean sectors
print('    Zonal average on ocean sectors...')
data2plot['observations'] = {}
for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

    zwda_avg = zonal_avg(zwda_woa, zarea_woa, longitudes=vocean)
    data2plot['observations'][kocean] = {'X' : zwda_avg.latitude.values, 
                                         'Y' : zwda_avg.depth.values, 
                                         'Z' : zwda_avg.values}

#

#________________
# Global average/sum woa
# Compute volume
depbounds, depthickness = compute_vertical_bounds_thickness(zwda_woa.depth)
omask = ~(zwda_woa.isnull())
volume_woa = omask* depthickness * zarea_woa 
del depbounds, depthickness
# Comute weighted average
zwda_woa_glob = zwda_woa.weighted(volume_woa).mean(dim=('k', 'j', 'i'), skipna=True).values


zwda_woa_glob_sum_tot = (zwda_woa*volume_woa).sum(dim=('k', 'j', 'i'), skipna=True)
zlim = 1000
zwda_woa_glob_sum_a = (compute_vertical_sum(zwda_woa, zmax=zlim)*zarea_woa).sum(dim=('j', 'i'), skipna=True)
zwda_woa_glob_sum_b = (compute_vertical_sum(zwda_woa, zmin=zlim)*zarea_woa).sum(dim=('j', 'i'), skipna=True)
data2plot['observations']['global sum'] = {
    'total': zwda_woa_glob_sum_tot / 1000., 
    'a'+str(zlim): zwda_woa_glob_sum_a.values / 1000., 
    'b'+str(zlim): zwda_woa_glob_sum_b.values / 1000.
} # in molO2

#--------------------
# Prepare ESM data for comparison with observation
#--------------------
print('Prepare ESM datas for comparison with observation...')
keep_esm_error = {}
data2plot['esm'] = {}
data2plot['esm_rgd'] = {}
data2plot['esm_error'] = {}

for iesm, vesm in enumerate(esm_list): 
    
    print(f'    For {vesm}...')
    
    #________________
    # Get data
    print('        Get data...')
    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1 = zwds1[var]*zfact_esm # in mmol.m-3
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1_rgd = zwds1[var]*zfact_esm # in mmol.m-3
        
    #________________
    # Compute error to observations
    print('        Compute error to observations...')
    zwda_err = zwda1_rgd - zwda_woa
    keep_esm_error[vesm] = zwda_err

    #_________________
    # Get area
    print('        Get area...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello'].load()
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    zarea_rgd = gridArea(zlon=zwda_err.longitude.values, zlat=zwda_err.latitude.values)

    #________________
    # Zonal average on ocean sectors
    print('        Zonal average on ocean sectors...')
    data2plot['esm'][vesm] = {}
    data2plot['esm_rgd'][vesm] = {}
    data2plot['esm_error'][vesm] = {}
    for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

        zwda_avg = zonal_avg(zwda1, zarea, longitudes=vocean)
        data2plot['esm'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                          'Y' : zwda_avg.depth.values, 
                                          'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwda1_rgd, zarea_rgd, longitudes=vocean)
        data2plot['esm_rgd'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                              'Y' : zwda_avg.depth.values, 
                                              'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwda_err, zarea_rgd, longitudes=vocean)
        data2plot['esm_error'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                                'Y' : zwda_avg.depth.values, 
                                                'Z' : zwda_avg.values}

    #
#

#________________
# Compute multimodel comparison with to observation
print('    Compute multimodel error to observation...')

aaa = []
for iesm, vesm in enumerate(esm_list): aaa.append(keep_esm_error[vesm])
bbb=xr.concat(aaa, dim='model')

# zwerror = np.sqrt((bbb*bbb).mean(dim='model')).where(~bbb.median(dim='model').isnull())
zwerror = bbb.mean(dim='model') 
# zwerror_spread = bbb.std(dim='model')
# zwerror_spread = bbb.max(dim='model') - bbb.min(dim='model')
# zwerror_spread = bbb.std(dim='model')/zwda_woa
# zwerror_spread = (bbb.max(dim='model') - bbb.min(dim='model'))/zwda_woa_glob
zwerror_spread = (bbb.max(dim='model') - bbb.min(dim='model'))/zwda_woa

data2plot['multimodel_error'] = {}
data2plot['multimodel_error_spread'] = {}
print('    Zonal average on ocean sectors...')
zarea = gridArea(zlon=zwerror.longitude.values, zlat=zwerror.latitude.values)
for iii, (kocean, vocean) in enumerate(ocean_dict.items()):
        zwda_avg = zonal_avg(zwerror, zarea, longitudes=vocean)
        data2plot['multimodel_error'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                 'Y' : zwda_avg.depth.values, 
                                                 'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwerror_spread, zarea, longitudes=vocean)
        data2plot['multimodel_error_spread'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                        'Y' : zwda_avg.depth.values, 
                                                        'Z' : zwda_avg.values}
#

#--------------------
# Prepare ESM data for assessing consistency of change
#--------------------
print('Prepare ESM data for assessing consistency of change...')
data2plot['esm_delta'] = {}
data2plot['esm_delta_rgd'] = {}
keep_esm_delta = {}

for iesm, vesm in enumerate(esm_list): 
    
    print(f'    For {vesm}...')
    
    #________________
    # Get data
    print('        Get data...')

    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1 = zwds1[var]*zfact_esm # in mmol.m-3

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'.nc'
    zwds3 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda3 = zwds3[var]*zfact_esm # in mmol.m-3

    simu = 'ssp585'
    tperiod = '2070-2099-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds2 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda2 = zwds2[var]*zfact_esm # in mmol.m-3

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'.nc'
    zwds4 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda4 = zwds4[var]*zfact_esm # in mmol.m-3

    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1_rgd = zwds1[var]*zfact_esm # in mmol.m-3

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'_regrided.nc'
    zwds3 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda3_rgd = zwds3[var]*zfact_esm # in mmol.m-3

    simu = 'ssp585'
    tperiod = '2070-2099-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds2 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda2_rgd = zwds2[var]*zfact_esm # in mmol.m-3

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'_regrided.nc'
    zwds4 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda4_rgd = zwds4[var]*zfact_esm # in mmol.m-3

    #________________
    # Compute delta
    print('        Compute delta...')
    zwda = zwda2 - zwda1 - (zwda4 - zwda3)
    zwda_rgd = zwda2_rgd - zwda1_rgd - (zwda4_rgd - zwda3_rgd)
    keep_esm_delta[vesm] = zwda_rgd

    #_________________
    # Get area
    print('        Get area...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello'].load()
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    zarea_rgd = gridArea(zlon=zwda_err.longitude.values, zlat=zwda_err.latitude.values)

    #________________
    # Zonal average on ocean sectors
    print('        Zonal average on ocean sectors...')
    data2plot['esm_delta'][vesm] = {}
    data2plot['esm_delta_rgd'][vesm] = {}
    for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

        zwda_avg = zonal_avg(zwda, zarea, longitudes=vocean)
        data2plot['esm_delta'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                                'Y' : zwda_avg.depth.values, 
                                                'Z' : zwda_avg.values}

        zwda_avg = zonal_avg(zwda_rgd, zarea_rgd, longitudes=vocean)
        data2plot['esm_delta_rgd'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                                    'Y' : zwda_avg.depth.values, 
                                                    'Z' : zwda_avg.values}

    #
#

#________________
# Compute multi-model mean and agreement
print('    Compute multi-model mean and agreement...')

aaa = []
for iesm, vesm in enumerate(esm_list): aaa.append(keep_esm_delta[vesm])
bbb=xr.concat(aaa, dim='model')

# Global average/sum esm delta
depbounds, depthickness = compute_vertical_bounds_thickness(bbb.isel(model=0).depth)
omask = ~(bbb.isel(model=0).isnull())
volume_rgd = omask* depthickness * zarea_rgd
del depbounds, depthickness
bbb_glob_sum = bbb.weighted(volume_rgd).sum(dim=('k', 'j', 'i'), skipna=True)
bbb_glob_mean = bbb.weighted(volume_rgd).mean(dim=('k', 'j', 'i'), skipna=True)
volume_rgd_glob_sum = volume_rgd.sum(dim=('k', 'j', 'i'), skipna=True)
volume_rgd_glob_mean = volume_rgd.mean(dim=('k', 'j', 'i'), skipna=True)

zwmean = bbb.mean(dim='model')

# zwagreement = bbb.std(dim='model')
# zwagreement = bbb.std(dim='model')/zwmean
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/zwmean
zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/np.abs(zwmean)
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))*volume_rgd/(bbb_glob_sum.max(dim='model') - bbb_glob_sum.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/bbb_glob_mean.mean(dim='model')
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))*volume_rgd/bbb_glob_sum.mean(dim='model')

data2plot['multimodel_mean_delta'] = {}
data2plot['multimodel_agreement_delta'] = {}
print('    Zonal average on ocean sectors...')
zarea = gridArea(zlon=zwmean.longitude.values, zlat=zwmean.latitude.values)
for iii, (kocean, vocean) in enumerate(ocean_dict.items()):
        zwda_avg = zonal_avg(zwmean, zarea, longitudes=vocean)
        data2plot['multimodel_mean_delta'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                      'Y' : zwda_avg.depth.values, 
                                                      'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwagreement, zarea, longitudes=vocean)
        data2plot['multimodel_agreement_delta'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                           'Y' : zwda_avg.depth.values,                                                            
                                                           'Z' : zwda_avg.values}
#


#--------------------
# Save data2plot in pickle
#--------------------
print('Save data2plot in pickle...')
savefile = dirout + 'data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#
print('File saved: '+savefile)
2025-05-05 16:29:17.713170
# AOU timeseries and validation and consistency
## Prepare data2plot
### For validation and consistency
Prepare WOA datas...
    Get data...
    Zonal average on ocean sectors...
Prepare ESM datas for comparison with observation...
    For MPI-ESM1-2-LR...
        Get data...
        Compute error to observations...
        Get area...
        Zonal average on ocean sectors...
    For ACCESS-ESM1-5...
        Get data...
        Compute error to observations...
        Get area...
        Zonal average on ocean sectors...
    For IPSL-CM6A-LR...
        Get data...
        Compute error to observations...
        Get area...
        Zonal average on ocean sectors...
    For MIROC-ES2L...
        Get data...
        Compute error to observations...
        Get area...
        Zonal average on ocean sectors...
    For NorESM2-LM...
        Get data...
        Compute error to observations...
        Get area...
        Zonal average on ocean sectors...
    Compute multimodel error to observation...
    Zonal average on ocean sectors...
Prepare ESM data for assessing consistency of change...
    For MPI-ESM1-2-LR...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For ACCESS-ESM1-5...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For IPSL-CM6A-LR...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For MIROC-ES2L...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For NorESM2-LM...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    Compute multi-model mean and agreement...
    Zonal average on ocean sectors...
Save data2plot in pickle...
File saved: 24-12-17-figures-for-storyline-v1/data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl
peak memory: 4991.82 MiB, increment: 3348.38 MiB
CPU times: user 8.99 s, sys: 4.35 s, total: 13.3 s
Wall time: 24.8 s

Plot: AOU timeseries, section from obs, error to obs, change

In [25]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Validation and consistency of AOU and ∆AOU')
print('## Plot: AOU time series, sections from obs, error to obs, change ')

#--------------------
# Figure param
#--------------------

fwidth_cm, fheight_cm = 15, 15
nrow, ncol = 4, 3
fig_scaling = 1
fsize = (fwidth_cm*cm2in*fig_scaling, fheight_cm*cm2in*fig_scaling) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize)

import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]

#--------------------
# Plot sections
#--------------------

savefile = dirout + 'data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot_sec = pickle.load(file)
#

ocean_list = ['Atlantic', 'Pacific', 'Indian']

units = data2plot_sec['units']

#______________
# figure param
a = cmcrameri.cm.batlow_r
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

kwpcm_obs = dict(cmap=newcmap, vmin=0, vmax=250)

a = cmcrameri.cm.bam
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

kwpcm_err2obs = dict(cmap=newcmap, vmin=-100, vmax=100)
dens = 3
kwpcm_err2obs_range = dict(colors='none', hatches=[dens*'.', dens*'.'], levels=[.7, 1e20])

a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

kwpcm_delta = dict(cmap=newcmap, vmin=-20, vmax=20)

dens = 3
kwpcm_consistency = dict(colors='none', hatches=[dens*'.', dens*'.'], levels=[3, 1e20])


pcm = np.zeros_like(ax)
for iocean, vocean in enumerate(ocean_list): 
    
    #________________
    # Observations
    
    irow = 0
    zax = ax[irow, iocean]
    
    X = data2plot_sec['observations'][vocean]['X']
    Y = data2plot_sec['observations'][vocean]['Y']
    Z = data2plot_sec['observations'][vocean]['Z']
    pcm[irow, iocean] = zax.pcolormesh(X, Y, Z, **kwpcm_obs)
    zax.set_title(vocean+' sector', loc='center')

    #________________
    # RMSE to obs
    
    irow = 1
    zax = ax[irow, iocean]
    
    X = data2plot_sec['multimodel_error'][vocean]['X']
    Y = data2plot_sec['multimodel_error'][vocean]['Y']
    Z = data2plot_sec['multimodel_error'][vocean]['Z']
    pcm[irow, iocean] = zax.pcolormesh(X, Y, Z, **kwpcm_err2obs)

    X = data2plot_sec['multimodel_error_spread'][vocean]['X']
    Y = data2plot_sec['multimodel_error_spread'][vocean]['Y']
    Z = np.abs(data2plot_sec['multimodel_error_spread'][vocean]['Z'])
    zax.contourf(X, Y, Z, **kwpcm_err2obs_range)

    #________________
    # change
    
    irow = 2
    zax = ax[irow, iocean]
    
    X = data2plot_sec['multimodel_mean_delta'][vocean]['X']
    Y = data2plot_sec['multimodel_mean_delta'][vocean]['Y']
    Z = data2plot_sec['multimodel_mean_delta'][vocean]['Z']
    pcm[irow, iocean] = zax.pcolormesh(X, Y, Z, **kwpcm_delta)

    X = data2plot_sec['multimodel_agreement_delta'][vocean]['X']
    Y = data2plot_sec['multimodel_agreement_delta'][vocean]['Y']
    Z = np.abs(data2plot_sec['multimodel_agreement_delta'][vocean]['Z'])
    zax.contourf(X, Y, Z, **kwpcm_consistency)
#

#--------------------
# Plot time series
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
esm_list2 = ['CanESM5', 'CNRM-ESM2-1', 'GFDL-ESM4', 'UKESM1-0-LL']
zlim = 1000
timeseries = 'nodrift'
dep_list = ['total', f'a{zlim}', f'b{zlim}']
zfact = 1e-15 * 117/170 * 12.01 # mol-O -> PgC
units = 'PgC'


for idep, vdep in enumerate(dep_list): 
    zw = []
    for iesm, vesm in enumerate(esm_list2): 
    
        savefile = dirout + 'data2plot_aou_timeseries_'+vesm+'.pckl'
        # Open the pickle file in binary read mode
        with open(savefile, 'rb') as file:
            # Load the dictionary from the pickle file
            data2plot_ts = pickle.load(file)
        #
        X = data2plot_ts[timeseries][vdep]['X']
        Y = data2plot_ts[timeseries][vdep]['Y']*zfact
        zw.append(Y-Y[0])
        
        if iesm==0: 
            print('\n----------------')
            print(f'    {vdep}    ')
            print('----------------\n')
        #
        print(f'    {vesm}: {np.round(Y[-1]-Y[0], decimals=0)}')
    #

    zw = np.array(zw)
    Ymin = np.min(zw, axis=0)      
    Ymax = np.max(zw, axis=0)
    zax=ax[-1, idep]
    ll1 = zax.fill_between(X, Ymin, Ymax, color='gray', lw=0, alpha=.2)
#

ll=[]
for iesm, vesm in enumerate(esm_list): 
    
    savefile = dirout + 'data2plot_aou_timeseries_'+vesm+'.pckl'
    # Open the pickle file in binary read mode
    with open(savefile, 'rb') as file:
        # Load the dictionary from the pickle file
        data2plot_ts = pickle.load(file)
    #
    
    for idep, vdep in enumerate(dep_list): 
        
        X = data2plot_ts[timeseries][vdep]['X']
        Y = data2plot_ts[timeseries][vdep]['Y']*zfact
        
        zax=ax[-1, idep]
        zwll, = zax.plot(X, Y-Y[0], label=esm_name_dict[vesm], c=esm_color_dict[vesm])
        if idep==0: ll.append(zwll)
        
        if idep==0: 
            print('\n----------------')
            print(f'    {vesm}    ')
            print('----------------\n')
        #
        print(f'    {vdep}: {np.round(Y[-1]-Y[0], decimals=0)}')
    #
#
leglab = [zwll.get_label() for zwll in ll]
ll.append(ll1)
leglab.append('Other ESMs')

#--------------------
fig.tight_layout()
#--------------------

#--------------------
# Decorate sections
#--------------------

#____________________
# Set axes position

for irow, axrow in enumerate(ax[1:-1, :], start=1): 
    for zax in axrow: 
        zw = zax.get_position()
        nx0 = zw.x0
        ny0 = zw.y0 + irow*0.05*zw.height
        nw = zw.width
        nh = zw.height
        zax.set_position([nx0, ny0, nw, nh])
    #
#
        
#____________________
# Set axes limits

for zax in ax[:-1, :].flat: 
    zax.set_ylim((5600, -50))
    zax.set_xlim((-90, 90))
    zax.text(-87, 5400, alphabet_reverse.pop()+')')
    zax.axhline(1000, ls='--', c='red', lw=1)
    # zax.label_outer()
#

#____________________
# Set axes labels

for zax in ax[:-1, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-2, :]: zax.set_xlabel('Latitude')

#____________________
# Set colorbars

for iax, zax in enumerate(ax[:-1, -1], start=0): 
    zw = zax.get_position()
    nx0 = zw.x1 + .1*zw.width
    ny0 = zw.y0
    nh = zw.height
    nw = .1*nh
    cax = fig.add_axes([nx0, ny0, nw, nh])
    cbar = fig.colorbar(pcm[iax, -1], cax=cax, orientation='vertical', \
                        ticklocation='right', extend='both')
    if iax==0: cbar.set_label('AOU from WOA23\n[mmol$\,$O$_2$ m$^{-3}$]')
    elif iax==1: cbar.set_label('Multi-model bias\n[mmol$\,$O$_2$ m$^{-3}$]')
    elif iax==2: cbar.set_label('Multi-model mean ∆AOU\n[mmol$\,$O$_2$ m$^{-3}$]')
    ttt=np.linspace(cbar.vmin, cbar.vmax, int(len(cbar.cmap.colors)/2)+1)
    cax.set_yticks(ttt)
#

#--------------------
# Decorate timeseries
#--------------------

#____________________
# Set axes position

for zax in ax[-1, :]: 
    zw = zax.get_position()
    nx0 = zw.x0
    ny0 = zw.y0 - .2*zw.height
    nw = zw.width
    nh = zw.height
    zax.set_position([nx0, ny0, nw, nh])
#

#____________________
# Set axes limits

ymin, ymax = [], []
for zax in ax[-1, :].flat: 
    zwymin, zwymax = zax.get_ylim()
    ymin.append(zwymin)
    ymax.append(zwymax)
#
yyymax = np.max(ymax)
yyymin = np.min(ymin)
for zax in ax[-1, :].flat: zax.set_ylim((yyymin, yyymax))


#____________________
# Set labels titles and legends

zax = ax[-1, 0]
zax.set_ylabel('Integrated ∆AOU [Pg$\,$C]')
zax = ax[-1, -1]
zax.legend(ll, leglab, handlelength=1, frameon=False, handletextpad=0.3, loc='center left', bbox_to_anchor=(1, .5))

ttl_list = [alphabet_reverse.pop()+') '+r'Global ocean', 
            alphabet_reverse.pop()+') '+r'Upper ocean (< 1000 m)', 
            alphabet_reverse.pop()+') '+r'Deep ocean (> 1000 m)']
for iax, zax in enumerate(ax[-1, :]): 
    zax.set_title(ttl_list[iax], loc='left')
    zax.set_xlabel('Years')
    zax.set_xlim((1848, 2101))
    zax.grid(axis='y', linestyle='--')
    zax.axvline(2015, c='gray', ls='--', lw=.5)
#

#--------------------
# Save figure
#--------------------

print('Save figure...')
figname = dirout + 'aou_timeseries_and_sections_of_obs_error_and_change.png'
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-05-05 16:29:52.734173
# Validation and consistency of AOU and ∆AOU
## Plot: AOU time series, sections from obs, error to obs, change 

----------------
    total    
----------------

    CanESM5: 21.0
    CNRM-ESM2-1: 114.0
    GFDL-ESM4: 42.0
    UKESM1-0-LL: 14.0

----------------
    a1000    
----------------

    CanESM5: 9.0
    CNRM-ESM2-1: 43.0
    GFDL-ESM4: -2.0
    UKESM1-0-LL: 13.0

----------------
    b1000    
----------------

    CanESM5: 12.0
    CNRM-ESM2-1: 71.0
    GFDL-ESM4: 44.0
    UKESM1-0-LL: 1.0

----------------
    MPI-ESM1-2-LR    
----------------

    total: 35.0
    a1000: 1.0
    b1000: 34.0

----------------
    ACCESS-ESM1-5    
----------------

    total: 49.0
    a1000: 12.0
    b1000: 36.0

----------------
    IPSL-CM6A-LR    
----------------

    total: 64.0
    a1000: 25.0
    b1000: 39.0

----------------
    MIROC-ES2L    
----------------

    total: 76.0
    a1000: 11.0
    b1000: 65.0

----------------
    NorESM2-LM    
----------------

    total: 20.0
    a1000: -10.0
    b1000: 29.0
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/aou_timeseries_and_sections_of_obs_error_and_change.png
peak memory: 6236.09 MiB, increment: 3071.93 MiB
CPU times: user 2.63 s, sys: 1.01 s, total: 3.64 s
Wall time: 2.53 s

Plot AOU section in each ESM

In [36]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Validation and consistency of AOU and ∆AOU')
print('## Plot AOU section in each ESM')

#______________
# Load data
savefile = dirout + 'data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#


#--------------------
# Figure parameters
#--------------------

figname = dirout + 'section_aou_esm_average_on_1971-2000.png'

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['Atlantic', 'Pacific', 'Indian']
zfact = 1000.

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.batlow_r
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_under(colors[0])
newcmap.set_over(colors[-1])
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=0, vmax=250)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]


#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot['esm'][vesm][vregion]['X']
        yyy = data2plot['esm'][vesm][vregion]['Y']
        ccc = data2plot['esm'][vesm][vregion]['Z']
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        zax.axhline(1000, ls='--', c='red', lw=1)
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ttl = extended_alphabet_reverse.pop() + f')'
        zax.text(-80, 800, ttl)
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, vregion, transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'AOU [mmol$\,$O$_2$ m$^{-3}$]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-29 11:01:17.202407
# Validation and consistency of AOU and ∆AOU
## Plot AOU section in each ESM
<string>:72: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_aou_esm_average_on_1986-2009.png
peak memory: 11496.19 MiB, increment: 5696.26 MiB
CPU times: user 2.07 s, sys: 924 ms, total: 2.99 s
Wall time: 1.97 s

Plot AOU bias section in each ESM

In [37]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Validation and consistency of AOU and ∆AOU')
print('## Plot AOU bias section in each ESM')

#______________
# Load data
savefile = dirout + 'data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#


#--------------------
# Figure parameters
#--------------------

figname = dirout + 'section_aou_bias_esm_average_on_1971-2000.png'

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['Atlantic', 'Pacific', 'Indian']
zfact = 1000.

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.bam
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=-100, vmax=100)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]


#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot['esm_error'][vesm][vregion]['X']
        yyy = data2plot['esm_error'][vesm][vregion]['Y']
        ccc = data2plot['esm_error'][vesm][vregion]['Z']
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        zax.axhline(1000, ls='--', c='red', lw=1)
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ttl = extended_alphabet_reverse.pop() + f')'
        zax.text(-80, 800, ttl)
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, vregion, transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'AOU bias [mmol$\,$O$_2$ m$^{-3}$]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-29 11:29:27.968514
# Validation and consistency of AOU and ∆AOU
## Plot AOU bias section in each ESM
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_aou_bias_esm_average_on_1986-2009.png
peak memory: 11496.13 MiB, increment: 5696.20 MiB
CPU times: user 1.99 s, sys: 949 ms, total: 2.93 s
Wall time: 1.81 s

Plot AOU change section in each ESM

In [39]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Validation and consistency of AOU and ∆AOU')
print('## Plot AOU change section in each ESM')

#______________
# Load data
savefile = dirout + 'data2plot_validation_and_consistency_of_aou_and_delta_aou.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#


#--------------------
# Figure parameters
#--------------------

figname = dirout + 'section_aou_change_esm.png'

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['Atlantic', 'Pacific', 'Indian']
zfact = 1000.

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=-20, vmax=20)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]


#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot['esm_delta'][vesm][vregion]['X']
        yyy = data2plot['esm_delta'][vesm][vregion]['Y']
        ccc = data2plot['esm_delta'][vesm][vregion]['Z']
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        zax.axhline(1000, ls='--', c='red', lw=1)
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ttl = extended_alphabet_reverse.pop() + f')'
        zax.text(-80, 800, ttl)
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, vregion, transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'∆AOU [mmol$\,$O$_2$ m$^{-3}$]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-29 11:36:49.861047
# Validation and consistency of AOU and ∆AOU
## Plot AOU change section in each ESM
<string>:72: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_aou_change_esm.png
peak memory: 11496.10 MiB, increment: 5696.16 MiB
CPU times: user 1.99 s, sys: 947 ms, total: 2.93 s
Wall time: 1.85 s

Age timeseries and consistency

Prepare data2plot

Age timeseries

In [9]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Age timeseries and consistency')
print('## Prepare data2plot')
print('### Age timeseries')

# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
esm_list = ['NorESM2-LM']
var='agessc'

for iesm, vesm in enumerate(esm_list): 
    
    print(f'For {vesm}...')
    
    data2plot = {}

    #________________
    # Get data
    print('    Get data...')

    simu = 'historical'
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    fname_list = glob.glob(fname)
    simu = 'ssp585'
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    fname_list.extend(glob.glob(fname))
    
    zwda = xr.open_mfdataset(fname_list, **kwopenmfds)[var] # in mol.m-3
    
    # simu = 'historical'
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_185[0-9].nc"
    # fname_list = glob.glob(fname)
    # zwda = xr.open_mfdataset(fname_list, **kwopenmfds)[var] # in mol.m-3

    #________________
    # Get drift
    print('    Get drift...')
    
    tperiod_drift = f'{picontrol_refyear_dict[vesm]:04}-{picontrol_refyear_dict[vesm]+(2099-1850):04}'
    fname = dirshared+vesm+'_piControl_'+var+'_linregress_on_'+tperiod_drift+'.nc'
     
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
     
    drift_slope  = zwds['slope'] # mol.m-3.yr-1
    drift_itcpt  = zwds['intercept'] 
    
    xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(2099-1850)+.5, 1, dtype='int')
    xxx_year_adjusted = np.arange(1850, 2099.5, 1, dtype='int')
    year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    drift = year * drift_slope + drift_itcpt
    
    # xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(1859-1850)+.5, 1, dtype='int')
    # xxx_year_adjusted = np.arange(1850, 1859.5, 1, dtype='int')
    # year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    # drift = year * drift_slope + drift_itcpt

    del zwds, drift_slope, drift_itcpt, xxx, xxx_year_adjusted, year
    
    #________________
    # Drift correction
    print('    Drift correction...')
    zwda_nodrift = zwda - drift
    
    #________________
    # FOR MPI model maybe
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda = zwda.isel(j=slice(None, None, -1))
        zwda_nodrift = zwda_nodrift.isel(j=slice(None, None, -1))
    #

    #_________________
    # Compute volume
    print('    Compute volume...')
    
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda.depth)
    
    omask = ~(zwda.isel(year=0, drop=True).isnull())
    volume = omask* depthickness * zarea 

    del depbounds, depthickness
    
    #_______________
    # Load
    # print('    Load...')
    # zwda.load()
    # zwda_nodrift.load()
    # zarea.load()
    # volume.load()
    
    data2plot['volume'] = {}
    data2plot['raw'] = {}
    data2plot['nodrift'] = {}
    
    #_______________
    # Total water column
    print('    Total water column...')
    data2plot['volume']['total'] = volume.sum(dim=('k', 'j', 'i'), skipna=True).values
    zw = (zwda*volume).sum(dim=('k', 'j', 'i'), skipna=True)
    data2plot['raw']['total'] = {'X': zw.year.values, 'Y': zw.values}
    zw = (zwda_nodrift*volume).sum(dim=('k', 'j', 'i'), skipna=True)
    data2plot['nodrift']['total'] = {'X': zw.year.values, 'Y': zw.values}
    
    #_______________
    # Above and below ???? meters
    # for zlim in [1000, 1500, 2000]: 
    for zlim in [1000]: 

        # above
        print(f'    Above {zlim} meters...')
        zw = (compute_vertical_sum(volume*0+1, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['volume']['a'+str(zlim)] = zw.values
        zw = (compute_vertical_sum(zwda, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['raw']['a'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        zw = (compute_vertical_sum(zwda_nodrift, zmax=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['nodrift']['a'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        del zw
        gc.collect()
        
        # below
        print(f'    Below {zlim} meters...')
        zw = (compute_vertical_sum(volume*0+1, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['volume']['b'+str(zlim)] = zw.values
        zw = (compute_vertical_sum(zwda, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['raw']['b'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        zw = (compute_vertical_sum(zwda_nodrift, zmin=zlim)*zarea).sum(dim=('j', 'i'), skipna=True)
        data2plot['nodrift']['b'+str(zlim)] = {'X': zw.year.values, 'Y': zw.values}
        
    #

    #_______________
    # Save data2plot in pickle
    print('    Save data2plot in pickle...')
    savefile = dirout + 'data2plot_agessc_timeseries_'+vesm+'.pckl'
    with open(savefile, "wb") as file:
        pickle.dump(data2plot, file)
    #
    print('    File saved: '+savefile)
    
    del zw, data2plot, zwda, zwda_nodrift, zarea, volume
    gc.collect()
#
2025-03-24 16:15:53.390560
# Age timeseries and consistency
## Prepare data2plot
### Age timeseries
For NorESM2-LM...
    Get data...
    Get drift...
    Drift correction...
    Compute volume...
    Total water column...
[########################################] | 100% Completed | 11.03 s
[########################################] | 100% Completed | 24.69 s
    Above 1000 meters...
[########################################] | 100% Completed | 16.91 s
[########################################] | 100% Completed | 16.36 s
[########################################] | 100% Completed | 31.29 s
[########################################] | 100% Completed | 31.16 s
[########################################] | 100% Completed | 15.67 s
    Below 1000 meters...
[########################################] | 100% Completed | 16.76 s
[########################################] | 100% Completed | 16.37 s
[########################################] | 100% Completed | 11.03 s
[########################################] | 100% Completed | 30.04 s
[########################################] | 100% Completed | 30.41 s
[########################################] | 100% Completed | 25.58 s
    Save data2plot in pickle...
    File saved: 24-12-17-figures-for-storyline-v1/data2plot_agessc_timeseries_NorESM2-LM.pckl
peak memory: 60013.82 MiB, increment: 59794.07 MiB
CPU times: user 8min 33s, sys: 5min 58s, total: 14min 31s
Wall time: 9min 17s

For consistency in ∆Age

Temporal mean and regriding

In [21]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Age timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For consistency in ∆Age')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
var='agessc'

# simu = 'ssp585'
# tperiod = slice('2070', '2099')
simu = 'historical'
tperiod = slice('1971', '2000')

# ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

#--------------------
# Get WOA datas
#--------------------
print('Get WOA datas...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
zwfname = 'woa23_all_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']

for iesm, vesm in enumerate(esm_list): 
    
    print(f'Prepare data for {vesm}...')
    
    #________________
    # Get data
    print('   Get data...')
    fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds)
    zwda1 = zwds1[var] # in mol.m-3
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda1 = zwda1.isel(j=slice(None, None, -1))
    #

    #_________________
    # Temporal average
    print('    Temporal average...')
    zwda1 = zwda1.sel(year=tperiod).mean(dim='year').compute()

    #_________________
    # Regrid
    print('    Regrid...')
    zwds = zwda1.to_dataset()
    zwda1_rgd_tmp = regrid(zwds, method='bilinear')[var]
    zwda1_rgd = vertical_interpolation(zwda1_rgd_tmp, zwda_woa.depth)

    #_________________
    # save in netcdf
    print('    Save in netcdf...')
    zwds2 = zwda1.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
    zwds2 = zwda1_rgd.to_dataset()
    zwds2.attrs = zwds1.attrs
    ymin = '%04d'%int(tperiod.start)
    ymax = '%04d'%int(tperiod.stop)
    ncname = netcdfdir+vesm+'_'+simu+'_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds2.to_netcdf(ncname)
    print('    File saved: '+ncname)
#
print('Done')
2025-03-24 16:37:23.220366
# Age timeseries and validation and consistency
## Prepare data2plot
### For consistency in ∆Age
Get WOA datas...
Prepare data for MPI-ESM1-2-LR...
   Get data...
    Temporal average...
    Regrid...
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_agessc_1971-2000-mean_regrided.nc
Prepare data for ACCESS-ESM1-5...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_agessc_1971-2000-mean_regrided.nc
Prepare data for IPSL-CM6A-LR...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_agessc_1971-2000-mean_regrided.nc
Prepare data for MIROC-ES2L...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_agessc_1971-2000-mean_regrided.nc
Prepare data for NorESM2-LM...
   Get data...
    Temporal average...
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_agessc_1971-2000-mean_regrided.nc
Done
peak memory: 7471.93 MiB, increment: 4230.64 MiB
CPU times: user 52 s, sys: 16.2 s, total: 1min 8s
Wall time: 4min 50s

For removing piControl drift in ∆Age

Temporal mean and regriding

In [12]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Age timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For removing piControl drift in ∆Age')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
var='agessc'

# ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

#--------------------
# Get WOA datas
#--------------------
print('Get WOA datas...')
# dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA18/'
# zwfname = 'woa18_all_A00_01.nc'
dirwoa= '/mnt/reef-ns1002k/daco/DATA_WOA/WOA23/'
zwfname = 'woa23_all_A00_01.nc'
kwopends['decode_times'] = False
zwds = xr.open_dataset(dirwoa + zwfname, **kwopends)
kwopends['decode_times'] = None
zwds = rename_vars_dims_coords(zwds, rename_dict)
zwds = split_coords_dimensions(zwds)
zlat = zwds.latitude
zlon = zwds.longitude
LON, LAT = np.meshgrid(zlon, zlat)
zwds = zwds.drop(['latitude', 'latitude']).assign_coords({'latitude':(['j', 'i'], LAT), 'longitude':(['j', 'i'], LON)})
zwds = shift_180_lon(zwds)
zwds = zwds.drop('time').squeeze()
zwda_woa = zwds['A_an']

for iesm, vesm in enumerate(esm_list): 
    
    print(f'Prepare data for {vesm}...')

    
    #________________
    # Get drift
    print('    Get drift...')
    
    tperiod_drift = f'{picontrol_refyear_dict[vesm]:04}-{picontrol_refyear_dict[vesm]+(2099-1850):04}'
    fname = dirshared+vesm+'_piControl_'+var+'_linregress_on_'+tperiod_drift+'.nc'
     
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
     
    drift_slope  = zwds['slope'] # mol.m-3.yr-1
    drift_itcpt  = zwds['intercept'] 
    
    xxx = np.arange(picontrol_refyear_dict[vesm], picontrol_refyear_dict[vesm]+(2099-1850)+.5, 1, dtype='int')
    xxx_year_adjusted = np.arange(1850, 2099.5, 1, dtype='int')
    year = xr.DataArray(xxx, dims='year', coords={'year': xxx_year_adjusted}, name='year')
    zwda = year * drift_slope + drift_itcpt
    zwda.name = var

    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda = zwda.isel(j=slice(None, None, -1))
    #

    #_________________
    # Temporal average
    print('    Temporal average...')
    tperiod1 = slice('1971', '2000')
    tperiod2 = slice('2070', '2099')
    zwda1 = zwda.sel(year=tperiod1).mean(dim='year').compute()
    zwda2 = zwda.sel(year=tperiod2).mean(dim='year').compute()

    #_________________
    # Regrid
    print('    Regrid...')
    
    zwds1 = zwda1.to_dataset()
    zwda1_rgd_tmp = regrid(zwds1, method='bilinear')[var]
    zwda1_rgd = vertical_interpolation(zwda1_rgd_tmp, zwda_woa.depth)

    zwds2 = zwda2.to_dataset()
    zwda2_rgd_tmp = regrid(zwds2, method='bilinear')[var]
    zwda2_rgd = vertical_interpolation(zwda2_rgd_tmp, zwda_woa.depth)

    #_________________
    # save in netcdf
    print('    Save in netcdf...')

    zwds_tosave = zwda1.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod1.start)
    ymax = '%04d'%int(tperiod1.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)
    
    zwds_tosave = zwda1_rgd.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod1.start)
    ymax = '%04d'%int(tperiod1.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

    zwds_tosave = zwda2.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod2.start)
    ymax = '%04d'%int(tperiod2.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

    zwds_tosave = zwda2_rgd.to_dataset()
    zwds_tosave.attrs = zwds.attrs
    ymin = '%04d'%int(tperiod2.start)
    ymax = '%04d'%int(tperiod2.stop)
    ncname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+ymin+'-'+ymax+'-mean_regrided.nc'
    zwds_tosave.to_netcdf(ncname)
    print('    File saved: '+ncname)

#
print('Done')
2025-03-24 16:31:27.297810
# Age timeseries and validation and consistency
## Prepare data2plot
### For removing piControl drift in ∆Age
Get WOA datas...
Prepare data for MPI-ESM1-2-LR...
    Get drift...
    Temporal average...
    Regrid...
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
/opt/conda/lib/python3.10/site-packages/xesmf/backend.py:38: UserWarning: Input array is not F_CONTIGUOUS. Will affect performance.
  warnings.warn('Input array is not F_CONTIGUOUS. ' 'Will affect performance.')
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_agessc_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_agessc_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_piControl_drift_agessc_2070-2099-mean_regrided.nc
Prepare data for ACCESS-ESM1-5...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 10.66 s
[########################################] | 100% Completed | 10.47 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_agessc_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_agessc_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_piControl_drift_agessc_2070-2099-mean_regrided.nc
Prepare data for IPSL-CM6A-LR...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 17.81 s
[########################################] | 100% Completed | 16.99 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_agessc_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_agessc_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_piControl_drift_agessc_2070-2099-mean_regrided.nc
Prepare data for MIROC-ES2L...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 10.94 s
[########################################] | 100% Completed | 11.04 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_agessc_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_agessc_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_piControl_drift_agessc_2070-2099-mean_regrided.nc
Prepare data for NorESM2-LM...
    Get drift...
    Temporal average...
[########################################] | 100% Completed | 18.55 s
[########################################] | 100% Completed | 18.45 s
    Regrid...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_agessc_1971-2000-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_agessc_1971-2000-mean_regrided.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_agessc_2070-2099-mean.nc
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_piControl_drift_agessc_2070-2099-mean_regrided.nc
Done
peak memory: 43167.00 MiB, increment: 40150.47 MiB
CPU times: user 1min 33s, sys: 56.1 s, total: 2min 29s
Wall time: 2min 31s

Prepare data2plot

In [23]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Age timeseries and validation and consistency')
print('## Prepare data2plot')
print('### For consistency')


esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
var='agessc'
zfact_esm = 1.
units = 'years'

ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

data2plot = {'units':units, 'ocean_dict':ocean_dict}

#--------------------
# Prepare ESM data for comparison with observation
#--------------------
print('Prepare ESM datas for comparison with observation...')
keep_esm = {}
data2plot['esm'] = {}
data2plot['esm_rgd'] = {}
data2plot['esm_error'] = {}

for iesm, vesm in enumerate(esm_list): 
    
    print(f'    For {vesm}...')
    
    #________________
    # Get data
    print('        Get data...')
    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1 = zwds1[var]*zfact_esm # in mmol.m-3
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1_rgd = zwds1[var]*zfact_esm # in mmol.m-3
        
    keep_esm[vesm] = zwda1_rgd

    #_________________
    # Get area
    print('        Get area...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello'].load()
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    zarea_rgd = gridArea(zlon=zwda1_rgd.longitude.values, zlat=zwda1_rgd.latitude.values)

    #________________
    # Zonal average on ocean sectors
    print('        Zonal average on ocean sectors...')
    data2plot['esm'][vesm] = {}
    data2plot['esm_rgd'][vesm] = {}
    data2plot['esm_error'][vesm] = {}
    for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

        zwda_avg = zonal_avg(zwda1, zarea, longitudes=vocean)
        data2plot['esm'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                          'Y' : zwda_avg.depth.values, 
                                          'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwda1_rgd, zarea_rgd, longitudes=vocean)
        data2plot['esm_rgd'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                              'Y' : zwda_avg.depth.values, 
                                              'Z' : zwda_avg.values}
    #
#

#________________
# Compute multimodel mean and spread
print('    Compute multimodel mean and spread...')

aaa = []
for iesm, vesm in enumerate(esm_list): aaa.append(keep_esm[vesm])
bbb=xr.concat(aaa, dim='model')

zwmean = bbb.mean(dim='model') 
zwspread = (bbb.max(dim='model') - bbb.min(dim='model'))/zwmean

data2plot['multimodel_mean'] = {}
data2plot['multimodel_spread'] = {}
print('    Zonal average on ocean sectors...')
zarea = gridArea(zlon=zwmean.longitude.values, zlat=zwmean.latitude.values)
for iii, (kocean, vocean) in enumerate(ocean_dict.items()):
        zwda_avg = zonal_avg(zwmean, zarea, longitudes=vocean)
        data2plot['multimodel_mean'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                'Y' : zwda_avg.depth.values, 
                                                'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwspread, zarea, longitudes=vocean)
        data2plot['multimodel_spread'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                  'Y' : zwda_avg.depth.values, 
                                                  'Z' : zwda_avg.values}
#

#--------------------
# Prepare ESM data for assessing consistency of change
#--------------------
print('Prepare ESM data for assessing consistency of change...')
data2plot['esm_delta'] = {}
data2plot['esm_delta_rgd'] = {}
keep_esm_delta = {}

for iesm, vesm in enumerate(esm_list): 
    
    print(f'    For {vesm}...')
    
    #________________
    # Get data
    print('        Get data...')

    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1 = zwds1[var]*zfact_esm # in years

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'.nc'
    zwds3 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda3 = zwds3[var]*zfact_esm # in years

    simu = 'ssp585'
    tperiod = '2070-2099-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+".nc"
    zwds2 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda2 = zwds2[var]*zfact_esm # in years

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'.nc'
    zwds4 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda4 = zwds4[var]*zfact_esm # in years

    simu = 'historical'
    tperiod = '1971-2000-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds1 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda1_rgd = zwds1[var]*zfact_esm # in years

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'_regrided.nc'
    zwds3 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda3_rgd = zwds3[var]*zfact_esm # in years

    simu = 'ssp585'
    tperiod = '2070-2099-mean'
    fname = netcdfdir+vesm+"_"+simu+"_"+var+"_"+tperiod+"_regrided.nc"
    zwds2 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda2_rgd = zwds2[var]*zfact_esm # in years

    fname = netcdfdir+vesm+'_piControl_drift_'+var+'_'+tperiod+'_regrided.nc'
    zwds4 = xr.open_mfdataset(fname, **kwopenmfds).load()
    zwda4_rgd = zwds4[var]*zfact_esm # in years

    #________________
    # Compute delta
    print('        Compute delta...')
    zwda = zwda2 - zwda1 - (zwda4 - zwda3)
    zwda_rgd = zwda2_rgd - zwda1_rgd - (zwda4_rgd - zwda3_rgd)
    keep_esm_delta[vesm] = zwda_rgd

    #_________________
    # Get area
    print('        Get area...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello'].load()
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    zarea_rgd = gridArea(zlon=zwda_rgd.longitude.values, zlat=zwda_rgd.latitude.values)

    #________________
    # Zonal average on ocean sectors
    print('        Zonal average on ocean sectors...')
    data2plot['esm_delta'][vesm] = {}
    data2plot['esm_delta_rgd'][vesm] = {}
    for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

        zwda_avg = zonal_avg(zwda, zarea, longitudes=vocean)
        data2plot['esm_delta'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                                'Y' : zwda_avg.depth.values, 
                                                'Z' : zwda_avg.values}

        zwda_avg = zonal_avg(zwda_rgd, zarea_rgd, longitudes=vocean)
        data2plot['esm_delta_rgd'][vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                                    'Y' : zwda_avg.depth.values, 
                                                    'Z' : zwda_avg.values}

    #
#

#________________
# Compute multi-model mean and agreement
print('    Compute multi-model mean and agreement...')

aaa = []
for iesm, vesm in enumerate(esm_list): aaa.append(keep_esm_delta[vesm])
bbb=xr.concat(aaa, dim='model')

# Global average/sum esm delta
depbounds, depthickness = compute_vertical_bounds_thickness(bbb.isel(model=0).depth)
omask = ~(bbb.isel(model=0).isnull())
volume_rgd = omask* depthickness * zarea_rgd
del depbounds, depthickness
bbb_glob_sum = bbb.weighted(volume_rgd).sum(dim=('k', 'j', 'i'), skipna=True)
bbb_glob_mean = bbb.weighted(volume_rgd).mean(dim=('k', 'j', 'i'), skipna=True)
volume_rgd_glob_sum = volume_rgd.sum(dim=('k', 'j', 'i'), skipna=True)
volume_rgd_glob_mean = volume_rgd.mean(dim=('k', 'j', 'i'), skipna=True)

zwmean = bbb.mean(dim='model')

# zwagreement = bbb.std(dim='model')
# zwagreement = bbb.std(dim='model')/zwmean
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/zwmean
zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/np.abs(zwmean)
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))*volume_rgd/(bbb_glob_sum.max(dim='model') - bbb_glob_sum.min(dim='model'))
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))/bbb_glob_mean.mean(dim='model')
# zwagreement = (bbb.max(dim='model') - bbb.min(dim='model'))*volume_rgd/bbb_glob_sum.mean(dim='model')

data2plot['multimodel_mean_delta'] = {}
data2plot['multimodel_agreement_delta'] = {}
print('    Zonal average on ocean sectors...')
zarea = gridArea(zlon=zwmean.longitude.values, zlat=zwmean.latitude.values)
for iii, (kocean, vocean) in enumerate(ocean_dict.items()):
        zwda_avg = zonal_avg(zwmean, zarea, longitudes=vocean)
        data2plot['multimodel_mean_delta'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                      'Y' : zwda_avg.depth.values, 
                                                      'Z' : zwda_avg.values}
        zwda_avg = zonal_avg(zwagreement, zarea, longitudes=vocean)
        data2plot['multimodel_agreement_delta'][kocean] = {'X' : zwda_avg.latitude.values, 
                                                           'Y' : zwda_avg.depth.values,                                                            
                                                           'Z' : zwda_avg.values}
#


#--------------------
# Save data2plot in pickle
#--------------------
print('Save data2plot in pickle...')
savefile = dirout + 'data2plot_consistency_of_agessc_and_delta_agessc.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#
print('File saved: '+savefile)
2025-03-24 16:44:01.917592
# Age timeseries and validation and consistency
## Prepare data2plot
### For consistency
Prepare ESM datas for comparison with observation...
    For MPI-ESM1-2-LR...
        Get data...
        Get area...
        Zonal average on ocean sectors...
    For ACCESS-ESM1-5...
        Get data...
        Get area...
        Zonal average on ocean sectors...
    For IPSL-CM6A-LR...
        Get data...
        Get area...
        Zonal average on ocean sectors...
    For MIROC-ES2L...
        Get data...
        Get area...
        Zonal average on ocean sectors...
    For NorESM2-LM...
        Get data...
        Get area...
        Zonal average on ocean sectors...
    Compute multimodel mean and spread...
    Zonal average on ocean sectors...
Prepare ESM data for assessing consistency of change...
    For MPI-ESM1-2-LR...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For ACCESS-ESM1-5...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For IPSL-CM6A-LR...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For MIROC-ES2L...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    For NorESM2-LM...
        Get data...
        Compute delta...
        Get area...
        Zonal average on ocean sectors...
    Compute multi-model mean and agreement...
    Zonal average on ocean sectors...
Save data2plot in pickle...
File saved: 24-12-17-figures-for-storyline-v1/data2plot_consistency_of_agessc_and_delta_agessc.pckl
peak memory: 8873.20 MiB, increment: 5004.16 MiB
CPU times: user 7.28 s, sys: 3.72 s, total: 11 s
Wall time: 22.3 s

Plot: Age timeseries, section, change

In [28]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Age timeseries and consistency')
print('## Plot: Age timeseries, section, change')

#--------------------
# Figure param
#--------------------

fwidth_cm, fheight_cm = 15, 11
nrow, ncol = 3, 3
fig_scaling = 1
fsize = (fwidth_cm*cm2in*fig_scaling, fheight_cm*cm2in*fig_scaling) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize)

import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]

#--------------------
# Plot sections
#--------------------

savefile = dirout + 'data2plot_consistency_of_agessc_and_delta_agessc.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#

ocean_list = ['Atlantic', 'Pacific', 'Indian']

units = data2plot['units']

#______________
# keywords
a = cmcrameri.cm.batlow_r
colors = a(np.linspace(0,1,10+1))
newcmap = ListedColormap(colors[:-1])
newcmap.set_over(colors[-1])
newcmap.set_under("grey")
newcmap.set_bad("none")

kwpcm_mean = dict(cmap=newcmap, vmin=0, vmax=2000)
dens = 3
kwpcm_spread = dict(colors='none', hatches=[dens*'.', dens*'.'], levels=[1., 1e20])

a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

kwpcm_delta = dict(cmap=newcmap, vmin=-100, vmax=100)
dens = 3
kwpcm_consistency = dict(colors='none', hatches=[dens*'.', dens*'.'], levels=[3, 1e20])


pcm = np.zeros_like(ax)
for iocean, vocean in enumerate(ocean_list): 
    
    #________________
    # Mean Age
    
    irow = 0
    zax = ax[irow, iocean]
    
    X = data2plot['multimodel_mean'][vocean]['X']
    Y = data2plot['multimodel_mean'][vocean]['Y']
    Z = data2plot['multimodel_mean'][vocean]['Z']
    pcm[irow, iocean] = zax.pcolormesh(X, Y, Z, **kwpcm_mean)

    X = data2plot['multimodel_spread'][vocean]['X']
    Y = data2plot['multimodel_spread'][vocean]['Y']
    Z = np.abs(data2plot['multimodel_spread'][vocean]['Z'])
    zax.contourf(X, Y, Z, **kwpcm_spread)

    zax.set_title(vocean+' sector', loc='center')

    #________________
    # change
    
    irow = 1
    zax = ax[irow, iocean]
    
    X = data2plot['multimodel_mean_delta'][vocean]['X']
    Y = data2plot['multimodel_mean_delta'][vocean]['Y']
    Z = data2plot['multimodel_mean_delta'][vocean]['Z']
    pcm[irow, iocean] = zax.pcolormesh(X, Y, Z, **kwpcm_delta)

    X = data2plot['multimodel_agreement_delta'][vocean]['X']
    Y = data2plot['multimodel_agreement_delta'][vocean]['Y']
    Z = np.abs(data2plot['multimodel_agreement_delta'][vocean]['Z'])
    zax.contourf(X, Y, Z, **kwpcm_consistency)
#

#--------------------
# Plot time series
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
zlim = 1000
timeseries = 'nodrift'
dep_list = ['total', f'a{zlim}', f'b{zlim}']
units = 'years'


ll=[]
for iesm, vesm in enumerate(esm_list): 
    
    savefile = dirout + 'data2plot_agessc_timeseries_'+vesm+'.pckl'
    # Open the pickle file in binary read mode
    with open(savefile, 'rb') as file:
        # Load the dictionary from the pickle file
        data2plot = pickle.load(file)
    #
    
    for idep, vdep in enumerate(dep_list): 
        
        volume = data2plot['volume'][vdep]
        X = data2plot[timeseries][vdep]['X']
        Y = data2plot[timeseries][vdep]['Y'] / volume
        
        zax=ax[-1, idep]
        zwll, = zax.plot(X, Y-Y[0], label=esm_name_dict[vesm], c=esm_color_dict[vesm])
        if idep==0: ll.append(zwll)

    #
#

leglab = [zwll.get_label() for zwll in ll]

#--------------------
fig.tight_layout()
#--------------------

#--------------------
# Decorate sections
#--------------------

#____________________
# Set axes position

for irow, axrow in enumerate(ax[1:-1, :], start=1): 
    for zax in axrow: 
        zw = zax.get_position()
        nx0 = zw.x0
        ny0 = zw.y0 + irow*0.05*zw.height
        nw = zw.width
        nh = zw.height
        zax.set_position([nx0, ny0, nw, nh])
    #
#
        
#____________________
# Set axes limits

for zax in ax[:-1, :].flat: 
    zax.set_ylim((5600, -50))
    zax.set_xlim((-90, 90))
    zax.text(-87, 5400, alphabet_reverse.pop()+')')
    zax.axhline(1000, ls='--', c='red', lw=1)
    # zax.label_outer()
#

#____________________
# Set axes labels

for zax in ax[:-1, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-2, :]: zax.set_xlabel('Latitude')

#____________________
# Set colorbars

for iax, zax in enumerate(ax[:-1, -1], start=0): 
    zw = zax.get_position()
    nx0 = zw.x1 + .1*zw.width
    ny0 = zw.y0
    nh = zw.height
    nw = .1*nh
    cax = fig.add_axes([nx0, ny0, nw, nh])
    cbar = fig.colorbar(pcm[iax, -1], cax=cax, orientation='vertical', \
                        ticklocation='right', extend='both')
    if iax==0: cbar.set_label('Multi-model mean\nAge [years]')
    elif iax==1: cbar.set_label('Multi-model mean\n∆Age [years]')
    ttt=np.linspace(cbar.vmin, cbar.vmax, int(len(cbar.cmap.colors)/2)+1)
    cax.set_yticks(ttt)
#

#--------------------
# Decorate timeseries
#--------------------

#____________________
# Set axes position

for zax in ax[-1, :]: 
    zw = zax.get_position()
    nx0 = zw.x0
    ny0 = zw.y0 - .2*zw.height
    nw = zw.width
    nh = zw.height
    zax.set_position([nx0, ny0, nw, nh])
#

#____________________
# Set axes limits

ymin, ymax = [], []
for zax in ax[-1, :].flat: 
    zwymin, zwymax = zax.get_ylim()
    ymin.append(zwymin)
    ymax.append(zwymax)
#
yyymax = np.max(ymax)
yyymin = np.min(ymin)
for zax in ax[-1, :].flat: zax.set_ylim((yyymin, yyymax))


#____________________
# Set labels titles and legends

zax = ax[-1, 0]
zax.set_ylabel('∆Age [years]')
zax = ax[-1, -1]
zax.legend(ll, leglab, handlelength=1, frameon=False, handletextpad=0.3, loc='center left', bbox_to_anchor=(1, .5))

ttl_list = [alphabet_reverse.pop()+') '+r'Global ocean', 
            alphabet_reverse.pop()+') '+r'Upper ocean (< 1000 m)', 
            alphabet_reverse.pop()+') '+r'Deep ocean (> 1000 m)']
for iax, zax in enumerate(ax[-1, :]): 
    zax.set_title(ttl_list[iax], loc='left')
    zax.set_xlabel('Years')
    zax.set_xlim((1848, 2101))
    zax.grid(axis='y', linestyle='--')
    zax.axvline(2015, c='gray', ls='--', lw=.5)
#

#--------------------
# Save figure
#--------------------

print('Save figure...')
figname = dirout + 'agessc_timeseries_and_sections_of_mean_and_change.png'
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-24 16:51:49.227369
# Age timeseries and consistency
## Plot: Age timeseries, section, change
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/agessc_timeseries_and_sections_of_mean_and_change.png
peak memory: 9420.43 MiB, increment: 4649.71 MiB
CPU times: user 2.04 s, sys: 948 ms, total: 2.98 s
Wall time: 1.96 s

Plot age change section in each ESM

In [10]:
%%time
%%memit -c
print(datetime.datetime.now())
print('################################')
print('################################')
print('# Age timeseries and consistency')
print('## Plot age change section in each ESM')
print('################################')

#______________
# Load data
savefile = dirout + 'data2plot_consistency_of_agessc_and_delta_agessc.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#


#--------------------
# Figure parameters
#--------------------

figname = dirout + 'section_agessc_change_esm.png'

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['Atlantic', 'Pacific', 'Indian']
zfact = 1000.

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=-100, vmax=100)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]


#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot['esm_delta'][vesm][vregion]['X']
        yyy = data2plot['esm_delta'][vesm][vregion]['Y']
        ccc = data2plot['esm_delta'][vesm][vregion]['Z']
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        zax.axhline(1000, ls='--', c='red', lw=1)
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ttl = extended_alphabet_reverse.pop() + f')'
        zax.text(-80, 800, ttl)
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, vregion, transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'∆Age [years]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)

print('################################')
print('Done: '+str(datetime.datetime.now()))
print('################################')
print('################################')
2025-05-08 19:07:27.998854
################################
################################
# Age timeseries and consistency
## Plot age change section in each ESM
################################
<string>:75: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_agessc_change_esm.png
################################
Done: 2025-05-08 19:07:29.658079
################################
################################
peak memory: 503.25 MiB, increment: 266.48 MiB
CPU times: user 2.14 s, sys: 859 ms, total: 2.99 s
Wall time: 1.92 s

Scatter plot ∆AOU vs ∆age and linear regression

Compute PO tracer average for water mass selection

In [9]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Compute PO tracer average for water mass selection')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'historical'
tslice = slice('1986', '2009')

for iesm, vesm in enumerate(esm_list): 

    print('Compute PO tracer for %s...'%vesm)
    
    #________________
    # Get data
    print('    Get data...')    
    
    var = 'po4'
    fname_list = [f"{dirshared}{vesm}_{simu}_{var}_{int(year):04}.nc" for year in np.arange(int(tslice.start), int(tslice.stop)+.5)]
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds) # in mol.m-3
    zwda1 = zwds[var].sel(year=tslice).mean(dim='year')

    var = 'o2'
    fname_list = [f"{dirshared}{vesm}_{simu}_{var}_{int(year):04}.nc" for year in np.arange(int(tslice.start), int(tslice.stop)+.5)]
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds) # in mol.m-3
    zwda2 = zwds[var].sel(year=tslice).mean(dim='year')

    #________________
    # Compute PO tracer
    print('    Compute PO tracer...')
    zwda_pot = 172*zwda1 + zwda2 # po-tracer = 172*po4+o2
    zwda_pot.name='po-tracer'

    #________________
    # create dataset
    zwda_ds = zwda_pot.to_dataset() 
    zwda_ds.attrs['description'] = 'PO tracer = 172*PO4 + O2'

    #________________
    # Save in netcdf
    print('    Save in netcdf...')
    ncname = netcdfdir+vesm+'_'+simu+f'_po_tracer_average_on_{tslice.start}-{tslice.stop}.nc'
    zwda_ds.to_netcdf(ncname)
    print('    File saved: %s'%ncname)
    
#
2025-01-06 11:31:29.593038
# Scatter plot ∆AOU vs ∆age and linear regression
## Compute PO tracer average for water mass selection
Compute PO tracer for MPI-ESM1-2-LR...
    Get data...
    Compute PO tracer...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_po_tracer_average_on_1986-2009.nc
Compute PO tracer for ACCESS-ESM1-5...
    Get data...
    Compute PO tracer...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_po_tracer_average_on_1986-2009.nc
Compute PO tracer for IPSL-CM6A-LR...
    Get data...
    Compute PO tracer...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_po_tracer_average_on_1986-2009.nc
Compute PO tracer for MIROC-ES2L...
    Get data...
    Compute PO tracer...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_po_tracer_average_on_1986-2009.nc
Compute PO tracer for NorESM2-LM...
    Get data...
    Compute PO tracer...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_po_tracer_average_on_1986-2009.nc
peak memory: 2118.88 MiB, increment: 1891.69 MiB
CPU times: user 20.9 s, sys: 11.1 s, total: 32 s
Wall time: 1min 35s

Plot PO-tracer

In [35]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Plot PO-tracer')

#--------------------
# Prepare data2plot
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
data2plot={}
ocean_dict = {'Atlantic': (-60, -10), 'Pacific': (-180, -130), 'Indian':(40, 90)}

for iesm, vesm in enumerate(esm_list): 
    
    print(vesm+'...')
    
    data2plot[vesm] = {}    

    print('    Get po-tracer data...')
    fname = netcdfdir+vesm+'_historical_po_tracer_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds) # in mol.m-3
    zwda_pot = zwds['po-tracer']

    #_________________
    # Get area
    print('    Get area...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello'].load()
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    #________________
    # Zonal average on ocean sectors
    print('    Zonal average on ocean sectors...')
    for iii, (kocean, vocean) in enumerate(ocean_dict.items()):

        zwda_avg = zonal_avg(zwda_pot, zarea, longitudes=vocean)
        data2plot[vesm][kocean] = {'X' : zwda_avg.latitude.values, 
                                   'Y' : zwda_avg.depth.values, 
                                   'Z' : zwda_avg.values}
    #
#

#--------------------
# Figure parameters
#--------------------

figname = dirout + 'section_po-tracer_average_on_1986-2009.png'

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['Atlantic', 'Pacific', 'Indian']
zfact = 1000.

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.batlow_r
colors = a(np.linspace(0,1,10+1))
newcmap = ListedColormap(colors[:-1])
newcmap.set_under('grey')
newcmap.set_over(colors[-1])
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=0, vmax=1)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]


#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot[vesm][vregion]['X']
        yyy = data2plot[vesm][vregion]['Y']
        ccc = data2plot[vesm][vregion]['Z']
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        zwct = zax.contour(xxx, yyy, ccc, [0.4, 0.5], colors='k')
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ttl = extended_alphabet_reverse.pop() + f')'
        zax.text(-80, 800, ttl)
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, vregion, transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'PO$^*$ [mmol$\,$O$_2$ m$^{-3}$]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-29 11:00:07.618618
# Scatter plot ∆AOU vs ∆age and linear regression
## Plot PO-tracer
MPI-ESM1-2-LR...
    Get po-tracer data...
    Get area...
    Zonal average on ocean sectors...
ACCESS-ESM1-5...
    Get po-tracer data...
    Get area...
    Zonal average on ocean sectors...
IPSL-CM6A-LR...
    Get po-tracer data...
    Get area...
    Zonal average on ocean sectors...
MIROC-ES2L...
    Get po-tracer data...
    Get area...
    Zonal average on ocean sectors...
NorESM2-LM...
    Get po-tracer data...
    Get area...
    Zonal average on ocean sectors...
<string>:110: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_po-tracer_average_on_1986-2009.png
peak memory: 11629.92 MiB, increment: 5797.46 MiB
CPU times: user 4.4 s, sys: 1.36 s, total: 5.76 s
Wall time: 25.2 s

Compute density average for water mass selection

In [10]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression group by density')
print('## Compute density average for water mass selection')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'historical'
tslice = slice('1986', '2009')

for iesm, vesm in enumerate(esm_list): 

    print('Compute for %s...'%vesm)
    
    #________________
    # Get data
    print('    Get data...')    
    
    var = 'thetao'
    fname_list = [f"{dirshared}{vesm}_{simu}_{var}_{int(year):04}.nc" for year in np.arange(int(tslice.start), int(tslice.stop)+.5)]
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds).sel(year=tslice)
    zwda_temp = zwds[var]

    var = 'so'
    fname_list = [f"{dirshared}{vesm}_{simu}_{var}_{int(year):04}.nc" for year in np.arange(int(tslice.start), int(tslice.stop)+.5)]
    # fname = dirshared+vesm+"_"+simu+"_"+var+"_[0-9][0-9][0-9][0-9].nc"
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds).sel(year=tslice)
    zwda_sali = zwds[var]

    
    #________________
    # Compute density
    print('    Compute density...')
    zwda_sigma0 = compute_potential_density_anomaly(zwda_temp, zwda_sali).mean(dim='year')
    
    #________________
    # create dataset
    zwda_ds = zwda_sigma0.to_dataset() 

    #________________
    # Save in netcdf
    print('    Save in netcdf...')
    ncname = netcdfdir+f'{vesm}_{simu}_sigma0_average_on_{tslice.start}-{tslice.stop}.nc'
    zwda_ds.to_netcdf(ncname)
    print('    File saved: %s'%ncname)
    
#
2025-01-06 11:33:05.022908
# Scatter plot ∆AOU vs ∆age and linear regression group by density
## Compute density average for water mass selection
Compute for MPI-ESM1-2-LR...
    Get data...
    Compute density...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MPI-ESM1-2-LR_historical_sigma0_average_on_1986-2009.nc
Compute for ACCESS-ESM1-5...
    Get data...
    Compute density...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/ACCESS-ESM1-5_historical_sigma0_average_on_1986-2009.nc
Compute for IPSL-CM6A-LR...
    Get data...
    Compute density...
    Save in netcdf...
[########################################] | 100% Completed | 12.22 s
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/IPSL-CM6A-LR_historical_sigma0_average_on_1986-2009.nc
Compute for MIROC-ES2L...
    Get data...
    Compute density...
    Save in netcdf...
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/MIROC-ES2L_historical_sigma0_average_on_1986-2009.nc
Compute for NorESM2-LM...
    Get data...
    Compute density...
    Save in netcdf...
[########################################] | 100% Completed | 13.65 s
    File saved: 24-12-17-figures-for-storyline-v1/netcdf_files/NorESM2-LM_historical_sigma0_average_on_1986-2009.nc
peak memory: 4664.00 MiB, increment: 4062.33 MiB
CPU times: user 2min 7s, sys: 19.3 s, total: 2min 27s
Wall time: 2min 13s

Define function prep_data2plot

In [9]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Define function prep_data2plot')

def prep_data2plot(zaou, zage, zcond, zvolume, zomask, zcoloring_dict={}): 

    print('> prep_data2plot...')
    zdata2plot = {}

    #_________________
    # Select water mass   
    print('>> Select water mass...')    
    aou_in_water_mass = zaou.where(zcond, np.nan)
    age_in_water_mass = zage.where(zcond, np.nan)
    volume_in_water_mass = zvolume.where(zcond, np.nan)
    
    #_________________
    # Compute volume and number of cell
    print('>> Compute volume and number of cell')
    zdata2plot['volume'] = volume_in_water_mass.sum().values
    zdata2plot['cells'] = zcond.sum().values

    #_________________
    # Compute maps of water mass distribution
    print('>> Compute water mass distribution')

    zdata2plot['water mass distribution'] = {}

    zw = (xr.where(zcond, 1, 0)).where(zomask.values)    
    zvertical = compute_vertical_average(zw)
    zdata2plot['water mass distribution']['vertical average'] = {
        'xxx': zvertical.longitude.values, 
        'yyy': zvertical.latitude.values, 
        'ccc': zvertical.values
    }
    zzonal = zonal_avg(zw, zvolume)
    zdata2plot['water mass distribution']['zonal average'] = {
        'xxx': zzonal.latitude.values, 
        'yyy': zzonal.depth.values, 
        'ccc': zzonal.values
    }

    #_________________
    # Compute integrated trends
    print('>> Compute integrated trends')
    zdata2plot['integrated aou trend'] = (aou_in_water_mass*volume_in_water_mass).sum(skipna=True).values
    zdata2plot['integrated age trend'] = (age_in_water_mass*volume_in_water_mass).sum(skipna=True).values

    #_________________
    # Compute histograms 2D for entire selected water mass
    print('>> Compute histogram 2D')
    nbins1 = 200
    bounds = [[np.nanmin(age_in_water_mass.values.flatten()),
               np.nanmax(age_in_water_mass.values.flatten())], 
              [np.nanmin(aou_in_water_mass.values.flatten()), 
               np.nanmax(aou_in_water_mass.values.flatten())]]
    hhh = histogram2d(age_in_water_mass.values.flatten(), aou_in_water_mass.values.flatten(), \
                      range=bounds, bins=nbins1).T # need to transpose for visualisation purpose
    xxx = np.linspace(bounds[0][0], bounds[0][1], nbins1)
    yyy = np.linspace(bounds[1][0], bounds[1][1], nbins1)
    zdata2plot['histogram2d'] = {'xxx':xxx, 'yyy':yyy, 'ccc':hhh}

    #_______________
    # compute linear regression
    print('>> Compute linear regression...')
    X = age_in_water_mass.values.flatten()
    Y = aou_in_water_mass.values.flatten()
    index = ~(np.isnan(X) | np.isnan(Y))
    X = X[index]
    Y = Y[index]
    results = stats.linregress(X, Y)
    
    Y_predicted = results.intercept + results.slope * X
    nnn = len(X)  # Number of observations
    t_value = stats.t.ppf(1 - 0.025, nnn - 2)  # t-value for 95% confidence interval with n-2 degrees of freedom
    # Standard error of the predicted y-values (y_hat)
    s_err = np.sqrt(np.sum((Y - Y_predicted) ** 2) / (nnn - 2))    
    
    #_______________
    # Compute 95% confidence band
    
    # Subsampled X and Y predicted to compute 95% confidence band
    combined = list(zip(X, Y_predicted))
    if nnn>100: sample_size=100
    else: sample_size=nnn
    # Find the minimum and maximum elements based on the `y` values
    min_combined = min(combined, key=lambda pair: pair[0])
    max_combined = max(combined, key=lambda pair: pair[0])
    # Remove these elements from the combined list to sample the rest
    remaining_combined = [pair for pair in combined if pair != min_combined and pair != max_combined]
    # Sample the remaining elements
    remaining_sample_size = sample_size - 2
    import random
    sampled_remaining = random.sample(remaining_combined, remaining_sample_size)
    # Include the min and max elements in the final sample
    sampled_combined = [min_combined] + sampled_remaining + [max_combined]
    sampled_X, sampled_Y_predicted = zip(*sampled_combined)
    # Convert the sampled tuples back to lists (if needed)
    sampled_X = list(sampled_X)
    sampled_Y_predicted = list(sampled_Y_predicted)
    # Combine `xxx` and `yyy` and sort based on `xxx`
    xxx_yyy = sorted(zip(sampled_X, sampled_Y_predicted))
    # Unzip the sorted pairs
    sorted_xxx, sorted_yyy = zip(*xxx_yyy)
    sampled_X = np.array(sorted_xxx)
    sampled_Y_predicted = np.array(sorted_yyy)

    conf_interval = t_value * s_err * np.sqrt(1 / nnn + (sampled_X- np.mean(X))**2 / np.sum((X - np.mean(X))**2))
    lower_band = sampled_Y_predicted - conf_interval
    upper_band = sampled_Y_predicted + conf_interval

    pred_interval = t_value * s_err * np.sqrt(1 + 1 / nnn + (sampled_X- np.mean(X))**2 / np.sum((X - np.mean(X))**2))
    lower_pred_band = sampled_Y_predicted - pred_interval
    upper_pred_band = sampled_Y_predicted + pred_interval

    #_______________
    # Save linregress analysis
    zdata2plot['linregress'] = {}
    zdata2plot['linregress']['slope'] = results.slope
    zdata2plot['linregress']['intercept'] = results.intercept
    zdata2plot['linregress']['rvalue'] = results.rvalue
    zdata2plot['linregress']['pvalue'] = results.pvalue
    zdata2plot['linregress']['stderr'] = results.stderr
    zdata2plot['linregress']['intercept_stderr'] = results.intercept_stderr
    zdata2plot['linregress']['number of observation'] = nnn
    zdata2plot['linregress']['tvalue'] = t_value
    zdata2plot['linregress']['sampled_X'] = sampled_X
    zdata2plot['linregress']['sampled_Y_predicted'] = sampled_Y_predicted
    zdata2plot['linregress']['lower_band'] = lower_band
    zdata2plot['linregress']['upper_band'] = upper_band
    zdata2plot['linregress']['conf_interval'] = conf_interval
    zdata2plot['linregress']['stderr_pred'] = s_err
    zdata2plot['linregress']['lower_pred_band'] = lower_pred_band
    zdata2plot['linregress']['upper_pred_band'] = upper_pred_band
    zdata2plot['linregress']['conf_interval'] = pred_interval
    
    # xxx = np.array([age_in_water_mass.min(), age_in_water_mass.max()])
    # yyy = slope * xxx + intercept
    # zdata2plot['linregress']['line'] = {'xxx':xxx, 'yyy':yyy}

    #_______________
    # Compute mean of zcoloring_list
    if len(zcoloring_dict)>0: 
        print('>> Compute mean of zcoloring_list...')
        for kcoloring, vcoloring in zcoloring_dict.items():             
            print(f'>>> for {kcoloring}...')
            coloring_in_water_mass = vcoloring.where(zcond, np.nan)            
            zdata2plot[kcoloring] = coloring_2D_bins(age_in_water_mass.values, aou_in_water_mass.values, \
                                                     coloring_in_water_mass.values, nbin=nbins1)
        #
    #
   
    #_______________
    # Compute prediction and prediction error
    X3D = age_in_water_mass.values
    Y3D = aou_in_water_mass.values
    Y3D_predicted = results.intercept + results.slope * X3D
    Y3D_error = Y3D - Y3D_predicted
    Y3D_abserror = np.abs(Y3D - Y3D_predicted)
    
    # Create new DataArray
    aou_in_water_mass_predicted = xr.DataArray(
        Y3D_predicted,
        dims=aou_in_water_mass.dims,
        coords=aou_in_water_mass.coords,
        attrs=aou_in_water_mass.attrs
    )
    aou_in_water_mass_predicted.name = aou_in_water_mass.name

    aou_in_water_mass_error = xr.DataArray(
        Y3D_error,
        dims=aou_in_water_mass.dims,
        coords=aou_in_water_mass.coords,
        attrs=aou_in_water_mass.attrs
    )
    aou_in_water_mass_error.name = aou_in_water_mass.name
    
    aou_in_water_mass_abserror = xr.DataArray(
        Y3D_abserror,
        dims=aou_in_water_mass.dims,
        coords=aou_in_water_mass.coords,
        attrs=aou_in_water_mass.attrs
    )
    aou_in_water_mass_abserror.name = aou_in_water_mass.name


    zdata2plot['aou prediction'] = {}
    #_______________
    # Save prediction
    zdata2plot['aou prediction']['predicted'] = {}
    zdata2plot['aou prediction']['predicted']['3D'] = {
        'xxx': aou_in_water_mass.longitude.values, 
        'yyy': aou_in_water_mass.latitude.values, 
        'zzz': aou_in_water_mass.depth.values, 
        'ccc': Y3D_predicted
    }
    zvertical = compute_vertical_average(aou_in_water_mass_predicted)
    zdata2plot['aou prediction']['predicted']['vertical average'] = {
        'xxx': zvertical.longitude.values, 
        'yyy': zvertical.latitude.values, 
        'ccc': zvertical.values
    }
    zzonal = zonal_avg(aou_in_water_mass_predicted, zvolume)
    zdata2plot['aou prediction']['predicted']['zonal average'] = {
        'xxx': zzonal.latitude.values, 
        'yyy': zzonal.depth.values, 
        'ccc': zzonal.values
    }

    #_______________
    # Save prediction error
    zdata2plot['aou prediction']['error'] = {}
    zdata2plot['aou prediction']['error']['3D'] = {
        'xxx': aou_in_water_mass.longitude.values, 
        'yyy': aou_in_water_mass.latitude.values, 
        'zzz': aou_in_water_mass.depth.values, 
        'ccc': Y3D_error
    }

    zvertical = compute_vertical_average(aou_in_water_mass_error)
    zdata2plot['aou prediction']['error']['vertical average'] = {
        'xxx': zvertical.longitude.values, 
        'yyy': zvertical.latitude.values, 
        'ccc': zvertical.values
    }
    zzonal = zonal_avg(aou_in_water_mass_error, zvolume)
    zdata2plot['aou prediction']['error']['zonal average'] = {
        'xxx': zzonal.latitude.values, 
        'yyy': zzonal.depth.values, 
        'ccc': zzonal.values
    }

    #_______________
    # Save prediction error
    zdata2plot['aou prediction']['absolute error'] = {}
    zdata2plot['aou prediction']['absolute error']['3D'] = {
        'xxx': aou_in_water_mass.longitude.values, 
        'yyy': aou_in_water_mass.latitude.values, 
        'zzz': aou_in_water_mass.depth.values, 
        'ccc': Y3D_abserror
    }
    zvertical = compute_vertical_average(aou_in_water_mass_abserror)
    zdata2plot['aou prediction']['absolute error']['vertical average'] = {
        'xxx': zvertical.longitude.values, 
        'yyy': zvertical.latitude.values, 
        'ccc': zvertical.values
    }
    zzonal = zonal_avg(aou_in_water_mass_abserror, zvolume)
    zdata2plot['aou prediction']['absolute error']['zonal average'] = {
        'xxx': zzonal.latitude.values, 
        'yyy': zzonal.depth.values, 
        'ccc': zzonal.values
    }

    return zdata2plot
#
#----------------
# End define prep_data2plot
#----------------
2025-02-19 15:34:02.396555
# Scatter plot ∆AOU vs ∆age and linear regression
## Define function prep_data2plot
peak memory: 393.94 MiB, increment: 171.77 MiB
CPU times: user 63.7 ms, sys: 0 ns, total: 63.7 ms
Wall time: 167 ms

Prepare data2plot

In [10]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Prepare data2plot')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'historical'
tperiod = '1986-2009'
# simu = 'ssp585'
# tperiod = '2015-2099'

zfact_esm_aou = 1000. # mol.m-3 to mmol.m-3 = umol.l-1

number_of_years_in_picontrol = 2099-1850

#----------------
# Loop on ESM
#----------------

data2plot = {}
for iesm, vesm in enumerate(esm_list): 
    
    print('-----------------------')
    print('Prepare data for %s...'%vesm)
    print('-----------------------')

    data2plot[vesm] = {}    

    #----------------
    # Get data
    #----------------
    
    print('    Get aou data...')    
    fname = dirshared+vesm+'_'+simu+'_'+'aou_linregress_on_'+tperiod+'_drift_removed.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_aou = zwds['slope']*zfact_esm_aou 
    zwda_aou_pval = zwds['pvalue']
    
    print('    Get age data...')    
    fname = dirshared+vesm+'_'+simu+'_'+'agessc_linregress_on_'+tperiod+'_drift_removed.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_age = zwds['slope'] # yr.yr-1
    zwda_age_pval = zwds['pvalue']

    print('    Get density data...')
    fname = netcdfdir+vesm+'_historical_sigma0_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds) # in mol.m-3
    zwda_sigma0 = zwds['sigma0']

    print('    Get po-tracer data...')
    fname = netcdfdir+vesm+'_historical_po_tracer_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds) # in mol.m-3
    zwda_pot = zwds['po-tracer']
 
    # FOR MPI model maybe
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda_aou = zwda_aou.isel(j=slice(None, None, -1))
        zwda_age = zwda_age.isel(j=slice(None, None, -1))
        zwda_aou_pval = zwda_aou_pval.isel(j=slice(None, None, -1))
        zwda_age_pval = zwda_age_pval.isel(j=slice(None, None, -1))
        zwda_sigma0 = zwda_sigma0.isel(j=slice(None, None, -1))
        zwda_pot = zwda_pot.isel(j=slice(None, None, -1))
    #


    #----------------
    # Compute volume
    #----------------
    print('    Compute volume and omask...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda_sigma0.depth)
    
    omask = ~(zwda_sigma0.isnull()).rename('mask')
    volume = omask * depthickness * zarea 

    del depbounds, depthickness
   
    #----------------
    # Align coordinates
    #----------------
    print('    Align coordinates...')
    # use reindex if the coordinates match but are not aligned
    for zwcoords in zwda_aou.coords : 
        if zwcoords in zwda_age.coords: zwda_age[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_aou_pval.coords: zwda_aou_pval[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_age_pval.coords: zwda_age_pval[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_sigma0.coords: zwda_sigma0[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_pot.coords: zwda_pot[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in omask.coords: omask[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in volume.coords: volume[zwcoords] = zwda_aou[zwcoords]
    #

    #----------------
    # Define coloring dict
    #----------------
    print('    Define coloring dict...')
    dep3D = expand_coords(zwda_sigma0, 'depth')
    lat3D = expand_coords(zwda_sigma0, 'latitude')
    lon3D = expand_coords(zwda_sigma0, 'longitude')
    
    coloring_dict = {'depth':dep3D, 'latitude':lat3D, 'longitude':lon3D, 'sigma0':zwda_sigma0}
     
    #----------------
    # In different water mass
    #----------------
    
    #_________________
    # Total ocean
    print('    For total ocean...')
    COND_total = omask
    data2plot[vesm]['total'] = prep_data2plot(zwda_aou, zwda_age, COND_total, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Deep ocean
    print('    For deep ocean...')
    COND_deep = COND_total & (dep3D > 1000)
    data2plot[vesm]['deep'] = prep_data2plot(zwda_aou, zwda_age, COND_deep, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Deep ocean significant
    print('    For deep ocean significant...')
    COND_deep_signif = COND_deep &  (zwda_aou_pval<=.05) & (zwda_age_pval<=.05)
    data2plot[vesm]['deep significant'] = prep_data2plot(zwda_aou, zwda_age, COND_deep_signif, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Southern
    print('    For southern waters...')
    po_tracer_trshld = [0.5, 0.7]
    COND_southern = COND_deep_signif & (lat3D < 60) & \
            (zwda_pot > po_tracer_trshld[0]) & (zwda_pot <= po_tracer_trshld[1])
    data2plot[vesm]['southern'] = prep_data2plot(zwda_aou, zwda_age, COND_southern, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Density quantile for Southern waters
    quantiles = weighted_quantile(values=zwda_sigma0.where(COND_southern, np.nan).values.flatten(), quantiles=np.linspace(0, 1, 11), \
                                  sample_weight=volume.where(COND_southern, np.nan).values.flatten())
    #_________________
    # Southern densest
    print('    For southern dense waters...')
    COND_southern_dense = COND_southern & (zwda_sigma0 >= quantiles[5])
    data2plot[vesm]['southern dense'] = prep_data2plot(zwda_aou, zwda_age, COND_southern_dense, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Southern lightest
    print('    For southern light waters...')
    COND_southern_light = COND_southern & (zwda_sigma0 < quantiles[5])
    data2plot[vesm]['southern light'] = prep_data2plot(zwda_aou, zwda_age, COND_southern_light, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Atlantic
    print('    For atlantic waters...')
    po_tracer_trshld = [0.4, 0.5]
    COND_atlantic = COND_deep_signif & (lat3D < 60) & (lon3D>=-90) & (lon3D<=30) & \
            (zwda_pot > po_tracer_trshld[0]) & (zwda_pot <= po_tracer_trshld[1])
    data2plot[vesm]['atlantic'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Density quantile for Atlanntic waters
    quantiles = weighted_quantile(values=zwda_sigma0.where(COND_atlantic, np.nan).values.flatten(), quantiles=np.linspace(0, 1, 11), \
                                  sample_weight=volume.where(COND_atlantic, np.nan).values.flatten())
    #_________________
    # Atlantic densest
    print('    For atlantic dense waters...')
    COND_atlantic_dense = COND_atlantic & (zwda_sigma0 >= quantiles[5])
    data2plot[vesm]['atlantic dense'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic_dense, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Atlantic lightest
    print('    For atlantic light waters...')
    COND_atlantic_light = COND_atlantic & (zwda_sigma0 < quantiles[5])
    data2plot[vesm]['atlantic light'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic_light, volume, omask, zcoloring_dict=coloring_dict) 
    
    #_________________
    # Southern and Atlantic water mass
    print('    For southern and atlantic water mass...')
    COND_atl_south = COND_atlantic | COND_southern
    data2plot[vesm]['southern and atlantic'] = prep_data2plot(zwda_aou, zwda_age, COND_atl_south, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # The rest of deep and significant
    print('    For the rest of deep and significant...')
    COND_rest_deep_signif = COND_deep_signif & ~COND_atl_south
    data2plot[vesm]['reminder deep and significant'] = prep_data2plot(zwda_aou, zwda_age, COND_rest_deep_signif, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # The deep and non significant
    print('    For the deep and non significant...')
    COND_deep_non_signif = COND_deep & ~COND_deep_signif
    data2plot[vesm]['deep and non significant'] = prep_data2plot(zwda_aou, zwda_age, COND_deep_non_signif, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # The surface
    print('    For the surface...')
    COND_surf = COND_total & ~COND_deep
    data2plot[vesm]['surface'] = prep_data2plot(zwda_aou, zwda_age, COND_surf, volume, omask, zcoloring_dict=coloring_dict) 
#

#_________________
# Save
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#

print('Done')
2025-02-19 15:35:01.987560
# Scatter plot ∆AOU vs ∆age and linear regression
## Prepare data2plot
-----------------------
Prepare data for MPI-ESM1-2-LR...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep and significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the deep and non significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for ACCESS-ESM1-5...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep and significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the deep and non significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for IPSL-CM6A-LR...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep and significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the deep and non significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for MIROC-ES2L...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep and significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the deep and non significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for NorESM2-LM...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep and significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the deep and non significant...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
Done
peak memory: 21655.51 MiB, increment: 21433.21 MiB
CPU times: user 3min, sys: 1min 47s, total: 4min 48s
Wall time: 10min 4s

Including low significance point

Prepare data2plot

In [50]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Including low significance point')
print('### Prepare data2plot')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
simu = 'historical'
tperiod = '1986-2009'
# simu = 'ssp585'
# tperiod = '2015-2099'

zfact_esm_aou = 1000. # mol.m-3 to mmol.m-3 = umol.l-1

number_of_years_in_picontrol = 2099-1850

#----------------
# Loop on ESM
#----------------

data2plot = {}
for iesm, vesm in enumerate(esm_list): 
    
    print('-----------------------')
    print('Prepare data for %s...'%vesm)
    print('-----------------------')

    data2plot[vesm] = {}    

    #----------------
    # Get data
    #----------------
    
    print('    Get aou data...')    
    fname = dirshared+vesm+'_'+simu+'_'+'aou_linregress_on_'+tperiod+'_drift_removed.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_aou = zwds['slope']*zfact_esm_aou 
    
    print('    Get age data...')    
    fname = dirshared+vesm+'_'+simu+'_'+'agessc_linregress_on_'+tperiod+'_drift_removed.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_age = zwds['slope'] # yr.yr-1

    print('    Get density data...')
    fname = netcdfdir+vesm+'_historical_sigma0_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds) # in mol.m-3
    zwda_sigma0 = zwds['sigma0']

    print('    Get po-tracer data...')
    fname = netcdfdir+vesm+'_historical_po_tracer_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds) # in mol.m-3
    zwda_pot = zwds['po-tracer']
 
    # FOR MPI model maybe
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda_aou = zwda_aou.isel(j=slice(None, None, -1))
        zwda_age = zwda_age.isel(j=slice(None, None, -1))
        zwda_sigma0 = zwda_sigma0.isel(j=slice(None, None, -1))
        zwda_pot = zwda_pot.isel(j=slice(None, None, -1))
    #


    #----------------
    # Compute volume
    #----------------
    print('    Compute volume and omask...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda_sigma0.depth)
    
    omask = ~(zwda_sigma0.isnull()).rename('mask')
    volume = omask * depthickness * zarea 

    del depbounds, depthickness
   
    #----------------
    # Align coordinates
    #----------------
    print('    Align coordinates...')
    # use reindex if the coordinates match but are not aligned
    for zwcoords in zwda_aou.coords : 
        if zwcoords in zwda_age.coords: zwda_age[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_sigma0.coords: zwda_sigma0[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in zwda_pot.coords: zwda_pot[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in omask.coords: omask[zwcoords] = zwda_aou[zwcoords]
        if zwcoords in volume.coords: volume[zwcoords] = zwda_aou[zwcoords]
    #

    #----------------
    # Define coloring dict
    #----------------
    print('    Define coloring dict...')
    dep3D = expand_coords(zwda_sigma0, 'depth')
    lat3D = expand_coords(zwda_sigma0, 'latitude')
    lon3D = expand_coords(zwda_sigma0, 'longitude')
    
    coloring_dict = {'depth':dep3D, 'latitude':lat3D, 'longitude':lon3D, 'sigma0':zwda_sigma0}
     
    #----------------
    # In different water mass
    #----------------
    
    #_________________
    # Total ocean
    print('    For total ocean...')
    COND_total = omask
    data2plot[vesm]['total'] = prep_data2plot(zwda_aou, zwda_age, COND_total, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Deep ocean
    print('    For deep ocean...')
    COND_deep = COND_total & (dep3D > 1000)
    data2plot[vesm]['deep'] = prep_data2plot(zwda_aou, zwda_age, COND_deep, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Southern
    print('    For southern waters...')
    po_tracer_trshld = [0.5, 0.7]
    COND_southern = COND_deep & (lat3D < 60) & \
            (zwda_pot > po_tracer_trshld[0]) & (zwda_pot <= po_tracer_trshld[1])
    data2plot[vesm]['southern'] = prep_data2plot(zwda_aou, zwda_age, COND_southern, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Density quantile for Southern waters
    quantiles = weighted_quantile(values=zwda_sigma0.where(COND_southern, np.nan).values.flatten(), quantiles=np.linspace(0, 1, 11), \
                                  sample_weight=volume.where(COND_southern, np.nan).values.flatten())
    #_________________
    # Southern densest
    print('    For southern dense waters...')
    COND_southern_dense = COND_southern & (zwda_sigma0 >= quantiles[5])
    data2plot[vesm]['southern dense'] = prep_data2plot(zwda_aou, zwda_age, COND_southern_dense, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Southern lightest
    print('    For southern light waters...')
    COND_southern_light = COND_southern & (zwda_sigma0 < quantiles[5])
    data2plot[vesm]['southern light'] = prep_data2plot(zwda_aou, zwda_age, COND_southern_light, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Atlantic
    print('    For atlantic waters...')
    po_tracer_trshld = [0.4, 0.5]
    COND_atlantic = COND_deep & (lat3D < 60) & (lon3D>=-90) & (lon3D<=30) & \
            (zwda_pot > po_tracer_trshld[0]) & (zwda_pot <= po_tracer_trshld[1])
    data2plot[vesm]['atlantic'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Density quantile for Atlanntic waters
    quantiles = weighted_quantile(values=zwda_sigma0.where(COND_atlantic, np.nan).values.flatten(), quantiles=np.linspace(0, 1, 11), \
                                  sample_weight=volume.where(COND_atlantic, np.nan).values.flatten())
    #_________________
    # Atlantic densest
    print('    For atlantic dense waters...')
    COND_atlantic_dense = COND_atlantic & (zwda_sigma0 >= quantiles[5])
    data2plot[vesm]['atlantic dense'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic_dense, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # Atlantic lightest
    print('    For atlantic light waters...')
    COND_atlantic_light = COND_atlantic & (zwda_sigma0 < quantiles[5])
    data2plot[vesm]['atlantic light'] = prep_data2plot(zwda_aou, zwda_age, COND_atlantic_light, volume, omask, zcoloring_dict=coloring_dict) 
    
    #_________________
    # Southern and Atlantic water mass
    print('    For southern and atlantic water mass...')
    COND_atl_south = COND_atlantic | COND_southern
    data2plot[vesm]['southern and atlantic'] = prep_data2plot(zwda_aou, zwda_age, COND_atl_south, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # The rest of deep
    print('    For the rest of deep...')
    COND_rest_deep_signif = COND_deep & ~COND_atl_south
    data2plot[vesm]['reminder deep'] = prep_data2plot(zwda_aou, zwda_age, COND_rest_deep_signif, volume, omask, zcoloring_dict=coloring_dict) 

    #_________________
    # The surface
    print('    For the surface...')
    COND_surf = COND_total & ~COND_deep
    data2plot[vesm]['surface'] = prep_data2plot(zwda_aou, zwda_age, COND_surf, volume, omask, zcoloring_dict=coloring_dict) 
#

#_________________
# Save
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'_include_low_significant_trend.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#

print('Done')
2025-01-10 17:42:22.204988
# Scatter plot ∆AOU vs ∆age and linear regression
## Including low significance point
### Prepare data2plot
-----------------------
Prepare data for MPI-ESM1-2-LR...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for ACCESS-ESM1-5...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for IPSL-CM6A-LR...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for MIROC-ES2L...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
-----------------------
Prepare data for NorESM2-LM...
-----------------------
    Get aou data...
    Get age data...
    Get density data...
    Get po-tracer data...
    Compute volume and omask...
    Align coordinates...
    Define coloring dict...
    For total ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For deep ocean...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic dense waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For atlantic light waters...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For southern and atlantic water mass...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the rest of deep...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
    For the surface...
> prep_data2plot...
>> Select water mass...
>> Compute volume and number of cell
>> Compute water mass distribution
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
>> Compute integrated trends
>> Compute histogram 2D
>> Compute linear regression...
>> Compute mean of zcoloring_list...
>>> for depth...
>>> for latitude...
>>> for longitude...
>>> for sigma0...
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
/opt/conda/lib/python3.10/site-packages/dask/core.py:119: RuntimeWarning: invalid value encountered in true_divide
  return func(*(_execute_task(a, cache) for a in args))
Done
peak memory: 55442.81 MiB, increment: 30791.45 MiB
CPU times: user 4min 24s, sys: 2min 9s, total: 6min 34s
Wall time: 10min 27s

Plot: scatter plot

In [40]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Plot: scatter plot')

#____________________
# Load data2plot

# simu = 'historical'
# tperiod = '1986-2009'
simu = 'ssp585'
tperiod = '2015-2099'
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#

#--------------------
# Figure parameters
#--------------------

#____________________
# figname
figname = savefile.replace('data2plot', 'scatter_plot').replace('.pckl', '.png')

#____________________
# list
esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['atlantic light', 'atlantic dense', 'southern light', 'southern dense']

#____________________
# Figure
fwidth_cm, fheight_cm = 17, 11.3333
nrow, ncol = 2, 3
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)

#____________________
# labels
region_label_dict = {
    'southern dense': 'Southern\ndense waters', 
    'southern light': 'Southern\nlight waters', 
    'atlantic dense': 'Atlantic\ndense waters', 
    'atlantic light': 'Atlantic\nlight waters', 
}
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]


#____________________
# Colors and maps
a = cmcrameri.cm.lajolla
colors = a(np.linspace(0,1,250))
region_color_dict = {
    'southern dense': colors[50],
    'southern light': colors[50*2],
    'atlantic dense': colors[50*3],
    'atlantic light': colors[50*4]
} 

#______________
# cmap
a = cmcrameri.cm.devon
colors = a(np.linspace(0,1,200))
newcmap = ListedColormap(colors[50:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[49])
newcmap.set_bad("none")

# a = cm.Wistia
# colors = a(np.linspace(0,1,300))
# newcmap = ListedColormap(colors[30:-1])
# newcmap.set_over(colors[-1])
# newcmap.set_under(colors[29])
# newcmap.set_bad("none")

#____________________
# keywords args
kwpcm = {
    'southern and atlantic': dict(cmap = newcmap, norm=mpl.colors.LogNorm(vmin=1, vmax=1000)), 
    'total': dict(cmap=ListedColormap(['#D3D3D3']), norm=mpl.colors.LogNorm(vmin=1, vmax=1000))
}


xlim = (-10, 10)
ylim = ( -3,  3)

#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    

    zax = ax.flat[iesm]
    zax.axhline(0, lw=.5, c='k')
    zax.axvline(0, lw=.5, c='k')
    ttl = alphabet_reverse.pop()+') '+esm_name_dict[vesm]
    zax.set_title(ttl, loc='left')
    zax.set_xlim(xlim)
    zax.set_ylim(ylim)

    #____________________
    # Full scatter plot in background

    sel_scatter = 'total'
    xxx = data2plot[vesm][sel_scatter]['histogram2d']['xxx']
    yyy = data2plot[vesm][sel_scatter]['histogram2d']['yyy']
    ccc = data2plot[vesm][sel_scatter]['histogram2d']['ccc']
    zax.pcolormesh(xxx, yyy, ccc, **kwpcm[sel_scatter])

    #____________________
    # Selected scatter plot

    sel_scatter = 'southern and atlantic'
    xxx = data2plot[vesm][sel_scatter]['histogram2d']['xxx']
    yyy = data2plot[vesm][sel_scatter]['histogram2d']['yyy']
    ccc = data2plot[vesm][sel_scatter]['histogram2d']['ccc']
    zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm[sel_scatter])
    pcm.flat[iesm] = zwpcm

    #____________________
    # Add regression lines
    lines = []
    tab_row1 = []
    tab_row2 = []
    tab_row3 = []
    for region in region_list:

        xxx = data2plot[vesm][region]['linregress']['sampled_X']
        yyy = data2plot[vesm][region]['linregress']['sampled_Y_predicted']
        yyymin = data2plot[vesm][region]['linregress']['lower_band']
        yyymax = data2plot[vesm][region]['linregress']['upper_band']
        # yyymin = data2plot[vesm][region]['linregress']['lower_pred_band']
        # yyymax = data2plot[vesm][region]['linregress']['upper_pred_band']

        zax.fill_between(xxx, yyymin, yyymax, color=region_color_dict[region], alpha=0.5, lw=0)
        ll, = zax.plot(xxx, yyy, label=region_label_dict[region], color=region_color_dict[region], lw=1)
        lines.append(ll)

                
        #____________________
        # Save some notes for table
        slope = data2plot[vesm][region]['linregress']['slope']
        rvalue = data2plot[vesm][region]['linregress']['rvalue']
        ocean_frac = data2plot[vesm][region]['volume']/data2plot[vesm]['total']['volume']*100        
        tab_row1.append(f'{np.floor(rvalue**2*100)/100:.2f}')
        tab_row2.append(f'{ocean_frac:.0f}%')
        tab_row3.append(f'{slope:.2f}')
        
        
    #
    #____________________
    # add notes
    kwtext = dict(ha='center', va='center_baseline', fontsize=6)
    # kwtext = dict(ha='center', va='top', fontsize=6)
    ytxt = .35 #.97
    xtxt1 = .5+.09
    xtxt2 = xtxt1+.15
    xtxt3 = xtxt2+.15
    zax.text(xtxt1, ytxt, 'Slope'    , transform=zax.transAxes, color='k', **kwtext)
    zax.text(xtxt2, ytxt, '$R^2$'    , transform=zax.transAxes, color='k', **kwtext)
    zax.text(xtxt3, ytxt, 'Ocean\nfraction', transform=zax.transAxes, color='k', **kwtext)
    ytxt-=.1
    for iregion, vregion in enumerate(region_list): 
        zax.text(xtxt2, ytxt, tab_row1[iregion], transform=zax.transAxes, color=region_color_dict[vregion], **kwtext)
        zax.text(xtxt3, ytxt, tab_row2[iregion], transform=zax.transAxes, color=region_color_dict[vregion], **kwtext)
        zax.text(xtxt1, ytxt, tab_row3[iregion], transform=zax.transAxes, color=region_color_dict[vregion], **kwtext)
        ytxt-=.07
    #
    
#

fig.tight_layout()
        
#--------------------
# Decorate
#--------------------

ax.flat[-1].axis('off')

#____________________
# legend
handles = [plt.Line2D([0], [0], marker='None', linestyle='-', label=region_label_dict[region], color=region_color_dict[region], lw=2) for region in region_list]
zax = ax.flat[-1]
zax.legend(handles=handles, handlelength=0.5, frameon=False, handletextpad=1, loc='upper right', bbox_to_anchor=(1, 1))

#____________________
# axis labels
for zax in ax[: , 0]: zax.set_ylabel(f'AOU trends, {tperiod}\n'+r'[mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]')
for zax in ax[-1, :]: zax.set_xlabel(f'Age trends, {tperiod} '+r'[yr yr$^{-1}$]')
ax[0, -1].set_xlabel(f'Age trends, {tperiod} '+r'[yr yr$^{-1}$]')

#---------------------
# Colorbar
#---------------------

zw1 = ax.flat[-2].get_position()
nx0 = zw1.x1 + .2*zw1.width
ny0 = zw1.y0
nh  = zw1.height
nw  = 0.03*nh
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='vertical', \
                    ticklocation='right', extend='max')
cbar.set_label(r'Number of data point')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-29 14:26:33.972024
# Scatter plot ∆AOU vs ∆age and linear regression
## Plot: scatter plot
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/scatter_plot_aou_trend_vs_age_trend_ssp585_2015-2099.png
peak memory: 16657.81 MiB, increment: 10949.31 MiB
CPU times: user 4.46 s, sys: 7.87 s, total: 12.3 s
Wall time: 20 s

Plot water-masses mask

In [23]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Scatter plot ∆AOU vs ∆age and linear regression')
print('## Plot water-masses mask')

#____________________
# Load data2plot

# simu = 'historical'
# tperiod = '1986-2009'
simu = 'ssp585'
tperiod = '2015-2099'
# savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'_include_low_significant_trend.pckl'
# Open the pickle file in binary read mode
with open(savefile, 'rb') as file:
    # Load the dictionary from the pickle file
    data2plot = pickle.load(file)
#

figname = savefile.replace('data2plot', 'section_water_mass_distribution_zonal_average').replace('.pckl', '.png')

#--------------------
# Figure parameters
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
region_list = ['southern light', 'southern dense', 'atlantic light', 'atlantic dense']

#______________
# Figure size
nrow, ncol = len(esm_list), len(region_list)
fwidth_cm, fheight_cm = 4*ncol, 3*nrow
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height

#______________
# cmap
a = cmcrameri.cm.batlow_r
colors = a(np.linspace(0,1,10))
newcmap = ListedColormap(colors[:])
newcmap.set_over('lightgrey')
newcmap.set_under('lightgrey')
newcmap.set_bad("none")

#______________
# pcm keywords
kwpcm = dict(cmap=newcmap, vmin=0, vmax=1)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

region_label_dict = {
    'total': 'Total ocean', 
    'deep' : 'Deep ocean', 
    'deep significant' : 'Deep ocean\nsignificant', 
    'southern'  : 'Southern waters',  
    'southern dense': 'Southern\ndense waters', 
    'southern light': 'Southern\nlight waters', 
    'atlantic': 'Atlantic waters',
    'atlantic dense': 'Atlantic\ndense waters', 
    'atlantic light': 'Atlantic\nlight waters', 
    'southern and atlantic': 'Southern and\nAtlantic waters',
    'reminder deep and significant': 'Rest of deep\nocean significant', 
    'deep and non significant': 'Deep ocean\nnon-significant', 
    'surface': 'Upper ocean'
}

#______________
# Create figure and axes
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)


#--------------------
# Plot
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    for iregion, vregion in enumerate(region_list): 

        zax = ax[iesm, iregion]
        xxx = data2plot[vesm][vregion]['water mass distribution']['zonal average']['xxx']
        yyy = data2plot[vesm][vregion]['water mass distribution']['zonal average']['yyy']
        ccc = data2plot[vesm][vregion]['water mass distribution']['zonal average']['ccc']
        ccc = np.where(ccc>1, 1, ccc) # small fix for some values that are above 1
        ccc = np.where(ccc==0, -1, ccc) # small fix for having values equal to zero set negative for visualisation
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        pcm[iesm, iregion] = zwpcm
        zax.set_ylim((6000, -10))
        zax.set_xlim((-90, 90))

        ocean_frac = data2plot[vesm][vregion]['volume']/data2plot[vesm]['total']['volume']*100
        ttl = extended_alphabet_reverse.pop() + f') {ocean_frac:.0f}% of ocean'
        zax.text(-80, 800, ttl)
        # zax.set_title(ttl, loc='left')
    
    #
#

fig.tight_layout()

#--------------------
# Decorate
#--------------------

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.1, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#
for iregion, vregion in enumerate(region_list) : 
    zax = ax[0, iregion]
    zax.text(.5, 1.2, region_label_dict[vregion], transform=zax.transAxes, ha='center', va='bottom')
#
        
for zax in ax[:, 0]: zax.set_ylabel('Depth [m]')
for zax in ax[-1, :]: zax.set_xlabel('Latitude [Deg. N]')

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, 1].get_position()
nx0 = zw1.x0
ny0 = zw1.y0 - .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 0.06*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
cbar.set_label(r'Zonal fraction of grid cell')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-28 13:56:29.316462
# Scatter plot ∆AOU vs ∆age and linear regression
## Plot water-masses mask
<string>:93: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/section_water_mass_distribution_zonal_average_aou_trend_vs_age_trend_ssp585_2015-2099_include_low_significant_trend.png
peak memory: 33835.19 MiB, increment: 19611.90 MiB
CPU times: user 4.15 s, sys: 6.22 s, total: 10.4 s
Wall time: 16 s

Constraint plot

linreg(∆AOU vs ∆age) in SSP585 vs linreg(∆AOU vs ∆age) in historical

In [10]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Constrain plot')
print('## linreg(∆AOU vs ∆age) in SSP585 vs linreg(∆AOU vs ∆age) in historical')

#____________________
# figname
figname = dirout + f'constraint_plot_linreg∆AOUvs∆age_in_SSP585_vs_linreg∆AOUvs∆age_in_historical.png'

#--------------------
# Load data2plot
#--------------------
data2plot = {}

simu, tperiod = 'historical', '1986-2009'
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
with open(savefile, 'rb') as file:
    data2plot['XXX'] = pickle.load(file)
#
# data2plot['XXX']['title'] = r'Sensitivity of trends in AOU to trends in age [mmol.m$^{-3}$.yr$^{-1}$]'+f'\n{tperiod}, historical simulation'
data2plot['XXX']['title'] = f'OUR on years {tperiod}\n[mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]'
simu, tperiod = 'ssp585', '2015-2099'
savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
with open(savefile, 'rb') as file:
    data2plot['YYY'] = pickle.load(file)
#
# data2plot['YYY']['title'] = r'Sensitivity of trends in AOU to trends in age [mmol.m$^{-3}$.yr$^{-1}$]'+f'\n{tperiod}, SSP5-8.5 simulation'
data2plot['YYY']['title'] = f'OUR on years {tperiod}\n[mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]'

# suptitle = r'OUR [mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]'

#--------------------
# Figure param
#--------------------

#____________________
# list
esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L']
region_list = ['southern light', 'atlantic light', 'southern dense', 'atlantic dense']

#____________________
# Create figure
fwidth_cm, fheight_cm = 12, 12
nrow, ncol = 2, 2
fsize = (fwidth_cm*cm2in, fheight_cm*cm2in) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, squeeze=False)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

region_label_dict = {
    'southern dense': 'Southern dense waters', 
    'southern light': 'Southern light waters', 
    'atlantic dense': 'Atlantic dense waters', 
    'atlantic light': 'Atlantic light waters', 
}

#--------------------
# Plot
#--------------------

for iregion, vregion in enumerate(region_list): 

    print(f'\n----- {vregion} -----\n')
    zax = ax.flat[iregion]
    xxx_save, yyy_save = [], []
    if iregion==0: model_handles = []
    
    for iesm, vesm in enumerate(esm_list): 
        
        tvalue =          data2plot['XXX'][vesm][vregion]['linregress']['tvalue']
        xxx    =          data2plot['XXX'][vesm][vregion]['linregress']['slope']
        xerr   = tvalue * data2plot['XXX'][vesm][vregion]['linregress']['stderr']
        xrval  =          data2plot['XXX'][vesm][vregion]['linregress']['rvalue']
        tvalue =          data2plot['YYY'][vesm][vregion]['linregress']['tvalue']
        yyy    =          data2plot['YYY'][vesm][vregion]['linregress']['slope']
        yerr   = tvalue * data2plot['YYY'][vesm][vregion]['linregress']['stderr']
        yrval  =          data2plot['YYY'][vesm][vregion]['linregress']['rvalue']
        zax.errorbar(xxx, yyy, xerr=xerr, yerr=yerr, fmt='o', markersize=4, color=esm_color_dict[vesm])
        if xrval**2 < 0.7: zax.errorbar(xxx, yyy, xerr=xerr, yerr=yerr, fmt='_', markersize=8, color='r')        
        if yrval**2 < 0.7: zax.errorbar(xxx, yyy, xerr=xerr, yerr=yerr, fmt='|', markersize=8, color='r')        
        xxx_save.append(xxx)
        yyy_save.append(yyy)
        if iregion==0: model_handles.append(plt.Line2D([0], [0], marker='o', markersize=4,
                                                       color=esm_color_dict[vesm], linestyle='None', label=esm_name_dict[vesm]))
        print(f'{vesm}: {xxx:.3f}')
        
    #
    
    #______________
    # Add regression line

    # Combine `xxx` and `yyy` and sort based on `xxx`
    xxx_yyy = sorted(zip(xxx_save, yyy_save))
    # Unzip the sorted pairs
    sorted_xxx, sorted_yyy = zip(*xxx_yyy)
    xxx_save = np.array(sorted_xxx)
    yyy_save = np.array(sorted_yyy)
    
    results = stats.linregress(xxx_save, yyy_save)
    # results.slope
    # results.intercept
    # results.rvalue
    # results.pvalue
    # results.stderr
    # results.intercept_stderr

    predicted = results.intercept + results.slope * xxx_save
    n = len(xxx_save)  # Number of observations
    t_value = stats.t.ppf(1 - 0.025, n - 2)  # t-value for 95% confidence interval with n-2 degrees of freedom

    # Standard error of the predicted y-values (y_hat)
    s_err = np.sqrt(np.sum((yyy_save - predicted) ** 2) / (n - 2))
    conf_interval = t_value * s_err * np.sqrt(1 / n + (xxx_save - np.mean(xxx_save))**2 / np.sum((xxx_save - np.mean(xxx_save))**2))
    lower_band = predicted - conf_interval
    upper_band = predicted + conf_interval

    zax.fill_between(xxx_save, lower_band, upper_band, color='k', alpha=0.2, lw=0)
    zax.plot(xxx_save, predicted, color='k', lw=.5)
    # R2 = f'$R^2={np.floor(results.rvalue**2*100)/100:.2f}'
    R2 = rf'$R^2$={np.round(results.rvalue**2, decimals=2)}'
    pvalue = rf'p-value={np.round(results.pvalue, decimals=2):.2f}' if results.pvalue >= .01 else rf'p-value<0.01'
    zwtxt = R2+'\n'+pvalue
    #zwtxt = f"$R^2={R2}$\np-value: {results.pvalue:.2f}"
    zax.text(.05, .95, zwtxt, transform=zax.transAxes, ha='left', va='top', size=6, \
             bbox=dict(facecolor='white', edgecolor='black', linewidth=0.5, boxstyle='square,pad=0.5'))

    #______________
    # Decorate

    # zax.set_xlabel(f'{data2plot["XXX"]["title"]}')
    # zax.set_ylabel(f'{data2plot["YYY"]["title"]}')
    # zax.set_xlabel(f'Sensitivity of ∆AOU to ∆age\n{data2plot["XXX"]["tperiod"]}')
    # zax.set_ylabel(f'Sensitivity of ∆AOU to ∆age\n{data2plot["YYY"]["tperiod"]}')
    grid_color = '#b0b0b0'
    grid_linestyle = '--'
    grid_linewidth = .5
    zax.grid(True, which='major', axis='both', color=grid_color, linestyle=grid_linestyle, linewidth=grid_linewidth)
    ttl = alphabet_reverse.pop()+') '+region_label_dict[vregion]
    zax.set_title(ttl, loc='left')
    
    # Make same x and y range
    xlim0 = zax.get_xlim()
    ylim0 = zax.get_ylim()
    xylim = (np.min([xlim0, ylim0]), np.max([xlim0, ylim0]))
    zax.set_xlim(xylim)
    zax.set_ylim(xylim)
    
    # 1:1 line
    xxx = np.linspace(zax.get_xlim()[0], zax.get_xlim()[1], 10)
    zax.plot(xxx, xxx, c=grid_color, ls=grid_linestyle, lw=grid_linewidth)
    
    xlim = zax.get_xlim()
    ylim = zax.get_ylim()
    zax.set_aspect(abs( (xlim[1]- xlim[0])/(ylim[1]- ylim[0]) ))

    #______________
    # Add results from observations
    if vregion in ['southern dense', 'atlantic dense']: 
        if vregion in ['southern dense']: 
            nnn=23
            tval = stats.t.ppf(1 - 0.025, nnn - 2)
            x_line, x_uncertainty = 0.04, 0.0029*tval
        elif vregion in ['atlantic dense']: 
            nnn=20
            tval = stats.t.ppf(1 - 0.025, nnn - 2)
            x_line, x_uncertainty = 0.11, 0.0073*tval
        #
        zax.fill_betweenx(ylim, x1=x_line-x_uncertainty, x2=x_line+x_uncertainty, color='tab:blue', alpha=0.2, lw=0)
        zax.axvline(x=x_line, color='tab:blue', lw=.5)
    #
    if vregion == 'southern dense': 
        zax.text(x_line, 0.001, 'Observation', ha='center', va='bottom', size=6, color='tab:blue')

#


fig.tight_layout()

#--------------------
# Decorate
#--------------------

for zax in ax[:, 0]: 
    zax.set_ylabel(f'{data2plot["YYY"]["title"]}')
#
for zax in ax[-1, :]: 
    zax.set_xlabel(f'{data2plot["XXX"]["title"]}')
#

#__________________
# Add legend
ax.flat[0].legend(handles=model_handles, handlelength=0.5, frameon=False, borderaxespad=0, borderpad=0,
                   handletextpad=.5, loc='lower right', bbox_to_anchor=(1, 0.03))

#__________________
# Suptile
# fig.suptitle(suptitle, x=0.5, y=1, ha='center', va='bottom')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-24 14:08:50.527117
# Constrain plot
## linreg(∆AOU vs ∆age) in SSP585 vs linreg(∆AOU vs ∆age) in historical

----- southern light -----

MPI-ESM1-2-LR: 0.075
ACCESS-ESM1-5: 0.621
IPSL-CM6A-LR: 0.207
MIROC-ES2L: 0.115
NorESM2-LM: 0.117

----- atlantic light -----

MPI-ESM1-2-LR: 0.113
ACCESS-ESM1-5: 0.217
IPSL-CM6A-LR: 0.309
MIROC-ES2L: 0.198
NorESM2-LM: 0.218

----- southern dense -----

MPI-ESM1-2-LR: 0.057
ACCESS-ESM1-5: 0.140
IPSL-CM6A-LR: 0.176
MIROC-ES2L: 0.079
NorESM2-LM: 0.146

----- atlantic dense -----

MPI-ESM1-2-LR: 0.079
ACCESS-ESM1-5: 0.186
IPSL-CM6A-LR: 0.300
MIROC-ES2L: 0.137
NorESM2-LM: 0.135
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/constraint_plot_linreg∆AOUvs∆age_in_SSP585_vs_linreg∆AOUvs∆age_in_historical.png
peak memory: 21551.91 MiB, increment: 21244.87 MiB
CPU times: user 5.25 s, sys: 9.6 s, total: 14.9 s
Wall time: 28.8 s

Figure ∆AOU = OUR * ∆Age + intercept + residual

Prepare data2plot

In [16]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Prepare data2plot')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']


#----------------
# Loop on ESM
#----------------

data2plot = {}
for iesm, vesm in enumerate(esm_list): 
    
    print('-----------------------')
    print('Prepare data for %s...'%vesm)
    print('-----------------------')

    #----------------
    # Initalize data2plot
    #----------------

    data2plot[vesm] = {
        '1': {
            'period':[], \
            '∆AOU': {'total':[], 'deep':[], 'deep significant':[], \
                     'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern and atlantic significant': [], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[], \
                     'southern and atlantic': []}, \
            '∆Age': {'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern and atlantic significant': [], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[], \
                     'southern and atlantic': []}, \
            'intercept': {'southern dense significant':[], 'southern light significant':[], \
                          'atlantic dense significant':[], 'atlantic light significant':[], \
                          'southern and atlantic significant': [], \
                          'southern dense':[], 'southern light':[], \
                          'atlantic dense':[], 'atlantic light':[], \
                          'southern and atlantic': []}, \
            'residual': {'southern dense significant':[], 'southern light significant':[], \
                         'atlantic dense significant':[], 'atlantic light significant':[], \
                         'southern and atlantic significant': [], \
                         'southern dense':[], 'southern light':[], \
                         'atlantic dense':[], 'atlantic light':[], \
                         'southern and atlantic': []}, \
            'R2': {'southern dense significant':[], 'southern light significant':[], \
                   'atlantic dense significant':[], 'atlantic light significant':[], \
                   'southern and atlantic significant': [], \
                   'southern dense':[], 'southern light':[], \
                   'atlantic dense':[], 'atlantic light':[], \
                   'southern and atlantic': []}, \
            'OUR': {'southern dense significant':[], 'southern light significant':[], \
                    'atlantic dense significant':[], 'atlantic light significant':[], \
                    'southern and atlantic significant': [], \
                    'southern dense':[], 'southern light':[], \
                    'atlantic dense':[], 'atlantic light':[], \
                    'southern and atlantic': []}
        }, 
        '2': {
            'period':[], \
            '∆AOU': {'total':[], 'deep':[], 'deep significant':[], \
                     'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern and atlantic significant': [], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[], \
                     'southern and atlantic': []}, \
            '∆Age': {'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern and atlantic significant': [], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[], \
                     'southern and atlantic': []}, \
            'intercept': {'southern dense significant':[], 'southern light significant':[], \
                          'atlantic dense significant':[], 'atlantic light significant':[], \
                          'southern and atlantic significant': [], \
                          'southern dense':[], 'southern light':[], \
                          'atlantic dense':[], 'atlantic light':[], \
                          'southern and atlantic': []}, \
            'residual': {'southern dense significant':[], 'southern light significant':[], \
                         'atlantic dense significant':[], 'atlantic light significant':[], \
                         'southern and atlantic significant': [], \
                         'southern dense':[], 'southern light':[], \
                         'atlantic dense':[], 'atlantic light':[], \
                         'southern and atlantic': []}, \
            'R2': {'southern dense significant':[], 'southern light significant':[], \
                   'atlantic dense significant':[], 'atlantic light significant':[], \
                   'southern and atlantic significant': [], \
                   'southern dense':[], 'southern light':[], \
                   'atlantic dense':[], 'atlantic light':[], \
                   'southern and atlantic': []}, \
            'OUR': {'southern dense significant':[], 'southern light significant':[], \
                    'atlantic dense significant':[], 'atlantic light significant':[], \
                    'southern and atlantic significant': [], \
                    'southern dense':[], 'southern light':[], \
                    'atlantic dense':[], 'atlantic light':[], \
                    'southern and atlantic': []}
        }
    }
        
    #----------------
    # Loop on time period (data2plot['1'])
    #----------------
    
    data2plot_number = '1'
    
    #----------------
    # define period_list
    nyear = 50
    yearsend   = np.arange(2099, 1850, -nyear)
    yearsstart = np.arange(2099-(nyear-1), 1850-(nyear-1), -nyear)
    period_list = []
    for yend, ystart in zip(yearsend, yearsstart): 
        period_list.append((ystart, yend))
    #
    period_list.reverse()
    
    for iperiod, vperiod in enumerate(period_list): 
        
        print(f'For period {vperiod}...')
        tperiod = f'{vperiod[0]}-{vperiod[1]}'

        data2plot[vesm][data2plot_number]['period'].append(tperiod)
        
        #----------------
        # Load data
        #----------------
        
        print('    Load data...')
        savefile = '/mnt/reef-ns1002k/daco/MY_JUPYTER_NOTEBOOKS/OceanICU/25-02-05-daou-vs-dage-for-different-time-period/' + \
            f'{vesm}_distribution_and_linear_regression_of_aou_trend_vs_age_trend_{tperiod}.pckl'
        with open(savefile, 'rb') as file:
            data2process_significant = pickle.load(file)
        #
        
        savefile = '/mnt/reef-ns1002k/daco/MY_JUPYTER_NOTEBOOKS/OceanICU/25-02-05-daou-vs-dage-for-different-time-period/' + \
            f'{vesm}_distribution_and_linear_regression_of_aou_trend_vs_age_trend_{tperiod}_include_low_significant_trend.pckl'
        with open(savefile, 'rb') as file:
            data2process_with_low_significant = pickle.load(file)
        #
        
        #----------------
        # Loop on water-masses
        #----------------
        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 

            #________________
            # Significant water-masses
            
            zwaou    = data2process_significant[watermass]['integrated aou trend']
            zwage    = data2process_significant[watermass]['integrated age trend']
            zwour    = data2process_significant[watermass]['linregress']['slope']
            zwitcpt  = data2process_significant[watermass]['linregress']['intercept']
            zwr2     = data2process_significant[watermass]['linregress']['rvalue']**2
            zwvolume = data2process_significant[watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            data2plot[vesm][data2plot_number]['∆AOU'     ][watermass+' significant'].append(zwaou)
            data2plot[vesm][data2plot_number]['∆Age'     ][watermass+' significant'].append(zw_delta_age)
            data2plot[vesm][data2plot_number]['intercept'][watermass+' significant'].append(zw_intercept)
            data2plot[vesm][data2plot_number]['residual' ][watermass+' significant'].append(zw_residual)
            data2plot[vesm][data2plot_number]['OUR'      ][watermass+' significant'].append(zwour)
            data2plot[vesm][data2plot_number]['R2'       ][watermass+' significant'].append(zwr2)
       
            #________________
            # Including low significant water-masses
            
            zwaou    = data2process_with_low_significant[watermass]['integrated aou trend']
            zwage    = data2process_with_low_significant[watermass]['integrated age trend']
            zwour    = data2process_with_low_significant[watermass]['linregress']['slope']
            zwitcpt  = data2process_with_low_significant[watermass]['linregress']['intercept']
            zwr2     = data2process_with_low_significant[watermass]['linregress']['rvalue']**2
            zwvolume = data2process_with_low_significant[watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
            data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age)
            data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept)
            data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual)
            data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour)
            data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2)
        
        #
        
        #----------------
        # Southern and atlantic significant
        #----------------
        
        zwaou_SA, zw_delta_age_SA, zw_intercept_SA, zw_residual_SA = 0, 0, 0, 0
        zwour_SA, zwr2_SA, zwvolume_SA = 0, 0, 0        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 
            
            zwaou    = data2process_significant[watermass]['integrated aou trend']
            zwage    = data2process_significant[watermass]['integrated age trend']
            zwour    = data2process_significant[watermass]['linregress']['slope']
            zwitcpt  = data2process_significant[watermass]['linregress']['intercept']
            zwr2     = data2process_significant[watermass]['linregress']['rvalue']**2
            zwvolume = data2process_significant[watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            zwaou_SA        += zwaou
            zw_delta_age_SA += zw_delta_age
            zw_intercept_SA += zw_intercept
            zw_residual_SA  += zw_residual

            zwour_SA    += zwour * zwvolume
            zwr2_SA     += zwr2 * zwvolume
            zwvolume_SA += zwvolume

        #
        zwour_SA = zwour_SA / zwvolume_SA
        zwr2_SA = zwr2_SA / zwvolume_SA

        watermass = 'southern and atlantic significant'
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou_SA)
        data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age_SA)
        data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept_SA)
        data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual_SA)
        data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour_SA)
        data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2_SA)

        #----------------
        # Southern and atlantic including low significant water-masses
        #----------------
        
        zwaou_SA, zw_delta_age_SA, zw_intercept_SA, zw_residual_SA = 0, 0, 0, 0
        zwour_SA, zwr2_SA, zwvolume_SA = 0, 0, 0        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 
            
            zwaou    = data2process_with_low_significant[watermass]['integrated aou trend']
            zwage    = data2process_with_low_significant[watermass]['integrated age trend']
            zwour    = data2process_with_low_significant[watermass]['linregress']['slope']
            zwitcpt  = data2process_with_low_significant[watermass]['linregress']['intercept']
            zwr2     = data2process_with_low_significant[watermass]['linregress']['rvalue']**2
            zwvolume = data2process_with_low_significant[watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            zwaou_SA        += zwaou
            zw_delta_age_SA += zw_delta_age
            zw_intercept_SA += zw_intercept
            zw_residual_SA  += zw_residual

            zwour_SA    += zwour * zwvolume
            zwr2_SA     += zwr2 * zwvolume
            zwvolume_SA += zwvolume

        #
        zwour_SA = zwour_SA / zwvolume_SA
        zwr2_SA = zwr2_SA / zwvolume_SA

        watermass = 'southern and atlantic'
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou_SA)
        data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age_SA)
        data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept_SA)
        data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual_SA)
        data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour_SA)
        data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2_SA)

        #----------------
        # Total, deep and deep significant (only ∆AOU)
        #----------------

        watermass = 'total'
        zwaou    = data2process_significant[watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
        watermass = 'deep'
        zwaou    = data2process_significant[watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass+' significant'].append(zwaou)
        watermass = 'deep'
        zwaou    = data2process_with_low_significant[watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
    #
    
    #----------------
    # 1986-2009 and 2015-2099 time periods (data2plot['2'])
    #----------------
    
    data2plot_number = '2'
    
    period_list = [(1986, 2009), (2015, 2099)]
    
    for iperiod, vperiod in enumerate(period_list): 
        
        print(f'For period {vperiod}...')
        tperiod = f'{vperiod[0]}-{vperiod[1]}'

        data2plot[vesm][data2plot_number]['period'].append(tperiod)
        
        #----------------
        # Load data
        #----------------

        if tperiod=='2015-2099': simu = 'ssp585'
        elif tperiod=='1986-2009': simu = 'historical'

        savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
        with open(savefile, 'rb') as file:
            data2process_significant = pickle.load(file)
        #
        savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'_include_low_significant_trend.pckl'
        with open(savefile, 'rb') as file:
            data2process_with_low_significant = pickle.load(file)
        #
        
        #----------------
        # Loop on water-masses
        #----------------
        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 

            #________________
            # Significant water-masses
            
            zwaou    = data2process_significant[vesm][watermass]['integrated aou trend']
            zwage    = data2process_significant[vesm][watermass]['integrated age trend']
            zwour    = data2process_significant[vesm][watermass]['linregress']['slope']
            zwitcpt  = data2process_significant[vesm][watermass]['linregress']['intercept']
            zwr2     = data2process_significant[vesm][watermass]['linregress']['rvalue']**2
            zwvolume = data2process_significant[vesm][watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            data2plot[vesm][data2plot_number]['∆AOU'     ][watermass+' significant'].append(zwaou)
            data2plot[vesm][data2plot_number]['∆Age'     ][watermass+' significant'].append(zw_delta_age)
            data2plot[vesm][data2plot_number]['intercept'][watermass+' significant'].append(zw_intercept)
            data2plot[vesm][data2plot_number]['residual' ][watermass+' significant'].append(zw_residual)
            data2plot[vesm][data2plot_number]['OUR'      ][watermass+' significant'].append(zwour)
            data2plot[vesm][data2plot_number]['R2'       ][watermass+' significant'].append(zwr2)
       
            #________________
            # Including low significant water-masses
            
            zwaou    = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
            zwage    = data2process_with_low_significant[vesm][watermass]['integrated age trend']
            zwour    = data2process_with_low_significant[vesm][watermass]['linregress']['slope']
            zwitcpt  = data2process_with_low_significant[vesm][watermass]['linregress']['intercept']
            zwr2     = data2process_with_low_significant[vesm][watermass]['linregress']['rvalue']**2
            zwvolume = data2process_with_low_significant[vesm][watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
            data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age)
            data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept)
            data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual)
            data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour)
            data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2)
        
        #
        
        #----------------
        # Southern and atlantic significant
        #----------------
        
        zwaou_SA, zw_delta_age_SA, zw_intercept_SA, zw_residual_SA = 0, 0, 0, 0
        zwour_SA, zwr2_SA, zwvolume_SA = 0, 0, 0        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 
            
            zwaou    = data2process_significant[vesm][watermass]['integrated aou trend']
            zwage    = data2process_significant[vesm][watermass]['integrated age trend']
            zwour    = data2process_significant[vesm][watermass]['linregress']['slope']
            zwitcpt  = data2process_significant[vesm][watermass]['linregress']['intercept']
            zwr2     = data2process_significant[vesm][watermass]['linregress']['rvalue']**2
            zwvolume = data2process_significant[vesm][watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            zwaou_SA        += zwaou
            zw_delta_age_SA += zw_delta_age
            zw_intercept_SA += zw_intercept
            zw_residual_SA  += zw_residual

            zwour_SA    += zwour * zwvolume
            zwr2_SA     += zwr2 * zwvolume
            zwvolume_SA += zwvolume

        #
        zwour_SA = zwour_SA / zwvolume_SA
        zwr2_SA = zwr2_SA / zwvolume_SA

        watermass = 'southern and atlantic significant'
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou_SA)
        data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age_SA)
        data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept_SA)
        data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual_SA)
        data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour_SA)
        data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2_SA)

        #----------------
        # Southern and atlantic including low significant water-masses
        #----------------
        
        zwaou_SA, zw_delta_age_SA, zw_intercept_SA, zw_residual_SA = 0, 0, 0, 0
        zwour_SA, zwr2_SA, zwvolume_SA = 0, 0, 0        
        for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 
            
            zwaou    = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
            zwage    = data2process_with_low_significant[vesm][watermass]['integrated age trend']
            zwour    = data2process_with_low_significant[vesm][watermass]['linregress']['slope']
            zwitcpt  = data2process_with_low_significant[vesm][watermass]['linregress']['intercept']
            zwr2     = data2process_with_low_significant[vesm][watermass]['linregress']['rvalue']**2
            zwvolume = data2process_with_low_significant[vesm][watermass]['volume']
            
            zw_delta_age = zwour * zwage
            zw_intercept = zwitcpt * zwvolume 
            zw_residual = zwaou - zw_delta_age - zw_intercept

            zwaou_SA        += zwaou
            zw_delta_age_SA += zw_delta_age
            zw_intercept_SA += zw_intercept
            zw_residual_SA  += zw_residual

            zwour_SA    += zwour * zwvolume
            zwr2_SA     += zwr2 * zwvolume
            zwvolume_SA += zwvolume

        #
        zwour_SA = zwour_SA / zwvolume_SA
        zwr2_SA = zwr2_SA / zwvolume_SA

        watermass = 'southern and atlantic'
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou_SA)
        data2plot[vesm][data2plot_number]['∆Age'     ][watermass].append(zw_delta_age_SA)
        data2plot[vesm][data2plot_number]['intercept'][watermass].append(zw_intercept_SA)
        data2plot[vesm][data2plot_number]['residual' ][watermass].append(zw_residual_SA)
        data2plot[vesm][data2plot_number]['OUR'      ][watermass].append(zwour_SA)
        data2plot[vesm][data2plot_number]['R2'       ][watermass].append(zwr2_SA)

        #----------------
        # Total, deep and deep significant (only ∆AOU)
        #----------------

        watermass = 'total'
        zwaou    = data2process_significant[vesm][watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
        watermass = 'deep'
        zwaou    = data2process_significant[vesm][watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass+' significant'].append(zwaou)
        watermass = 'deep'
        zwaou    = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
        data2plot[vesm][data2plot_number]['∆AOU'     ][watermass].append(zwaou)
    #
#

#----------------
# Save data2plot in pickle
#----------------

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_intercept_residual.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#
2025-03-06 12:04:32.584719
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Prepare data2plot
-----------------------
Prepare data for MPI-ESM1-2-LR...
-----------------------
For period (1850, 1899)...
    Load data...
For period (1900, 1949)...
    Load data...
For period (1950, 1999)...
    Load data...
For period (2000, 2049)...
    Load data...
For period (2050, 2099)...
    Load data...
For period (1986, 2009)...
For period (2015, 2099)...
-----------------------
Prepare data for ACCESS-ESM1-5...
-----------------------
For period (1850, 1899)...
    Load data...
For period (1900, 1949)...
    Load data...
For period (1950, 1999)...
    Load data...
For period (2000, 2049)...
    Load data...
For period (2050, 2099)...
    Load data...
For period (1986, 2009)...
For period (2015, 2099)...
-----------------------
Prepare data for IPSL-CM6A-LR...
-----------------------
For period (1850, 1899)...
    Load data...
For period (1900, 1949)...
    Load data...
For period (1950, 1999)...
    Load data...
For period (2000, 2049)...
    Load data...
For period (2050, 2099)...
    Load data...
For period (1986, 2009)...
For period (2015, 2099)...
-----------------------
Prepare data for MIROC-ES2L...
-----------------------
For period (1850, 1899)...
    Load data...
For period (1900, 1949)...
    Load data...
For period (1950, 1999)...
    Load data...
For period (2000, 2049)...
    Load data...
For period (2050, 2099)...
    Load data...
For period (1986, 2009)...
For period (2015, 2099)...
-----------------------
Prepare data for NorESM2-LM...
-----------------------
For period (1850, 1899)...
    Load data...
For period (1900, 1949)...
    Load data...
For period (1950, 1999)...
    Load data...
For period (2000, 2049)...
    Load data...
For period (2050, 2099)...
    Load data...
For period (1986, 2009)...
For period (2015, 2099)...
peak memory: 60895.56 MiB, increment: 40557.75 MiB
CPU times: user 15.5 s, sys: 1min 49s, total: 2min 4s
Wall time: 2min 16s

Plot

In [12]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot')

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_intercept_residual.pckl'
with open(savefile, 'rb') as file:
    data2plot = pickle.load(file)

#
watermass='southern and atlantic significant'

#____________________
# Set figname for saving figure

figname = dirout + 'barplot_AOUtrend_OURxAgetrend_intercept_residual.png'

#--------------------
# Figure parameters
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']

zfact = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC

label_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    # 'intercept'  : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    'intercept'  : r'Intercept',
    'residual'   : r'Residual'
}

color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'intercept' : cmcrameri.cm.batlowS.colors[1],
    'residual'  : cmcrameri.cm.batlowS.colors[2]
}

kwbar = dict(linewidth=0.5, align='center')
kwbar1 = dict(linewidth=.5, align='center', edgecolor='k')

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

ncol = 3
nrow = 2
fwidth_cm = 6
fheight_cm = 5
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize)

#--------------------
# Plot
#--------------------

for iesm, vesm in enumerate(esm_list): 
    
    zax = ax.flat[iesm]

    #--------------------
    # 50 year periods
    #--------------------

    number = '1'

    # Get x-ticks and xxx and www0 and www1
    x_ticks = [int(sss[:4]) for sss in data2plot[vesm][number]['period']]
    x_ticks.append(int(data2plot[vesm][number]['period'][-1][-4:])+1)
    xxx = (np.array(x_ticks[:-1])+np.array(x_ticks[1:]))/2
    
    www0 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.95
    www1 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.7
    
    
    #_________________
    # ∆AOU
    
    var = '∆AOU'
    
    hhh = np.array(data2plot[vesm][number][var]['total']) * zfact
    zax.bar(xxx, hhh, width=www0, label='Global ∆AOU', color=color_dict['Global '+var], edgecolor=color_dict['Global '+var], **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep']) * zfact
    # zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep significant']) * zfact
    # zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    zax.bar(xxx, hhh, width=www1, label=var, color=color_dict[var], edgecolor=color_dict[var], **kwbar)

    nnn = 3
    www2=www1/(nnn+1)
    
    #_________________
    # ∆Age
    iii = 0
    var = '∆Age'
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    bbb = 0*hhh
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    bars = zax.bar(xxx1, hhh, width=www2, color=color_dict[var], label=var, **kwbar1)

    #_________________
    # intercept
    iii = 1
    var = 'intercept'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], label=var, **kwbar1)

    #_________________
    # residual
    iii = 2
    var = 'residual'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], label=var, **kwbar1)
    
    #_________________
    # Add OUR
    # zax2  = zax.twinx()
    # hhh = data2plot[vesm][number]['OUR'][watermass]
    # zax2.plot(xxx, hhh, 'k-o')
    
    
    #_________________
    # Decorate
    
    # if iesm==0: zax.legend()
    
    ttl = alphabet_reverse.pop()+') '+esm_name_dict[vesm]
    zax.set_title(ttl, loc='left')
    zax.set_xticks(x_ticks)
    ylim = zax.get_ylim()
    dylim = 0.15*(ylim[1]-ylim[0])
    ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
    zax.set_ylim(ylim2)
    zax.axhline(0, c='k', lw=.5)

    #_________________
    # Add R2 text
    iii=0
    for ind, val in enumerate(data2plot[vesm][number]['R2'][watermass]): 
        # xtext = xxx[ind]-(nnn-(1+2*iii))*www2/2-www2/2
        # ytext = np.max([data2plot[vesm][number]['∆AOU'    ][watermass][ind],  data2plot[vesm][number]['∆Age'     ][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind] + \
        #                 data2plot[vesm][number]['residual'][watermass][ind], 0])
        # zax.text( xtext, ytext*zfact, f'{val:.2f}')
        xtext = xxx[ind]
        ytext = ylim2[0]+0.005*(ylim2[1]-ylim2[0])
        zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
    #
    xlim = zax.get_xlim()
    xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
    zax.text(xtext, ytext, r'$\overline{R^2}$:', ha='left', va='bottom', size=6)

#


fig.tight_layout()

#--------------------
# Decorate
#--------------------

ax.flat[-1].axis('off')

#____________________
# legend
    # if iesm==0: zax.legend()
zax=ax.flat[0]
aaa=zax.get_legend_handles_labels()
handles = aaa[0]
labels = [label_dict[vvv] for vvv in aaa[1]]
zax = ax.flat[-1]
zax.legend(handles, labels, frameon=False, handletextpad=1, loc='upper left', bbox_to_anchor=(0, 1))

#____________________
# axis labels
for zax in ax[: , 0]: zax.set_ylabel(r'[Pg$\,$C yr$^{-1}$]')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-11 10:12:02.351341
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/barplot_AOUtrend_OURxAgetrend_intercept_residual.png
peak memory: 494.39 MiB, increment: 226.83 MiB
CPU times: user 1.04 s, sys: 1.21 s, total: 2.25 s
Wall time: 1.11 s

Plot with 1986-2009 and 2015-2099

In [13]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot with 1986-2009 and 2015-2099')

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_intercept_residual.pckl'
with open(savefile, 'rb') as file:
    data2plot = pickle.load(file)

#
watermass='southern and atlantic significant'

#____________________
# Set figname for saving figure

figname = dirout + 'barplot_AOUtrend_OURxAgetrend_intercept_residual_with_1986-2009_and_2015-2099.png'

#--------------------
# Figure parameters
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']

zfact = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC

label_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    'intercept'  : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    'residual'   : r'residual'
}

color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'intercept' : cmcrameri.cm.batlowS.colors[1],
    'residual'  : cmcrameri.cm.batlowS.colors[2]
}

kwbar = dict(linewidth=0.5, align='center', alpha=.3)
kwbar1 = dict(linewidth=.5, align='center', edgecolor='k', alpha=.3)
kwbar12 = dict(linewidth=.5, align='center', edgecolor='k')

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

ncol = 3
nrow = 2
fwidth_cm = 6
fheight_cm = 5
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize)

#--------------------
# Plot
#--------------------

for iesm, vesm in enumerate(esm_list): 
    
    zax = ax.flat[iesm]

    #--------------------
    # 50 year periods
    #--------------------

    number = '1'

    # Get x-ticks and xxx and www0 and www1
    x_ticks = [int(sss[:4]) for sss in data2plot[vesm][number]['period']]
    x_ticks.append(int(data2plot[vesm][number]['period'][-1][-4:])+1)
    xxx = (np.array(x_ticks[:-1])+np.array(x_ticks[1:]))/2
    
    www0 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.95
    www1 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.7
    
    
    #_________________
    # ∆AOU
    
    var = '∆AOU'
    
    hhh = np.array(data2plot[vesm][number][var]['total']) * zfact
    zax.bar(xxx, hhh, width=www0, color=color_dict['Global '+var], edgecolor=color_dict['Global '+var], **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep']) * zfact
    # zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep significant']) * zfact
    # zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    zax.bar(xxx, hhh, width=www1, color=color_dict[var], edgecolor=color_dict[var], **kwbar)

    nnn = 3
    www2=www1/(nnn+1)
    
    #_________________
    # ∆Age
    iii = 0
    var = '∆Age'
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    bbb = 0*hhh
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, width=www2, color=color_dict[var], **kwbar1)

    #_________________
    # intercept
    iii = 1
    var = 'intercept'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], **kwbar1)

    #_________________
    # residual
    iii = 2
    var = 'residual'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], **kwbar1)
    
    #_________________
    # Decorate
    
    # if iesm==0: zax.legend()
    
    ttl = alphabet_reverse.pop()+') '+esm_name_dict[vesm]
    zax.set_title(ttl, loc='left')
    zax.set_xticks(x_ticks)
    ylim = zax.get_ylim()
    dylim = 0.1*(ylim[1]-ylim[0])
    ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
    zax.set_ylim(ylim2)
    zax.axhline(0, c='k', lw=.5)

    #_________________
    # Add R2 text
    iii=0
    for ind, val in enumerate(data2plot[vesm][number]['R2'][watermass]): 
        # xtext = xxx[ind]-(nnn-(1+2*iii))*www2/2-www2/2
        # ytext = np.max([data2plot[vesm][number]['∆AOU'    ][watermass][ind],  data2plot[vesm][number]['∆Age'     ][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind] + \
        #                 data2plot[vesm][number]['residual'][watermass][ind], 0])
        # zax.text( xtext, ytext*zfact, f'{val:.2f}')
        xtext = xxx[ind]
        ytext = ylim2[0]+0.01*(ylim2[1]-ylim2[0])
        zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
    #
    xlim = zax.get_xlim()
    xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
    zax.text(xtext, ytext, r'$R^2$:', ha='left', va='bottom', size=6)

    #--------------------
    # 1986-2009 and 2015-2099
    #--------------------

    number = '2'

    # Get x-ticks and xxx and www0 and www1
    x_ticks0 = [int(sss[:4]) for sss in data2plot[vesm][number]['period']]
    x_ticks0.append(int(data2plot[vesm][number]['period'][-1][-4:])+1)
    xxx0 = (np.array(x_ticks0[:-1])+np.array(x_ticks0[1:]))/2
    
    www0 = (np.array(x_ticks0[1:]) - np.array(x_ticks0[:-1])) * 0.95
    www1 = (np.array(x_ticks0[1:]) - np.array(x_ticks0[:-1])) * 0.7
    
    #_________________
    # ∆AOU
    var = '∆AOU'
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    zax.bar(xxx0, hhh, width=www0, color='none', label=var, **kwbar12)

    nnn = 3
    www2=www1/(nnn+1)
    
    #_________________
    # ∆Age
    iii = 0
    var = '∆Age'
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    bbb = 0*hhh
    xxx1 = xxx0-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, width=www2, color=color_dict[var], label=var, **kwbar12)

    #_________________
    # intercept
    iii = 1
    var = 'intercept'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx0-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], label=var, **kwbar12)

    #_________________
    # residual
    iii = 2
    var = 'residual'
    bbb += hhh
    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx0-(nnn-(1+2*iii))*www2/2
    zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=color_dict[var], label=var, **kwbar12)
    
    #_________________
    # Add R2 text
    iii=0
    for ind, val in enumerate(data2plot[vesm][number]['R2'][watermass]): 
        xtext = xxx0[ind]-(nnn-(1+2*iii))*www2[ind]/2-www2[ind]/2
        ytext = np.max([data2plot[vesm][number]['∆AOU'    ][watermass][ind],  data2plot[vesm][number]['∆Age'     ][watermass][ind], \
                        data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind], \
                        data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind] + \
                        data2plot[vesm][number]['residual'][watermass][ind], 0])
        # zax.text( xtext, ytext*zfact, f'{val:.2f}')
        zax.text( xtext, ytext*zfact, f'{val:.2f}', ha='center', va='bottom', size=6)
    #
#
fig.tight_layout()

#--------------------
# Decorate
#--------------------

ax.flat[-1].axis('off')

#____________________
# legend
    # if iesm==0: zax.legend()
zax=ax.flat[0]
aaa=zax.get_legend_handles_labels()
handles = aaa[0]
labels = [label_dict[vvv] for vvv in aaa[1]]
zax = ax.flat[-1]
zax.legend(handles, labels, frameon=False, handletextpad=1, loc='upper left', bbox_to_anchor=(0, 1))

#____________________
# axis labels
for zax in ax[: , 0]: zax.set_ylabel(r'[Pg$\,$C.year$^{-1}$]')


#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-11 10:12:09.034434
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot with 1986-2009 and 2015-2099
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/barplot_AOUtrend_OURxAgetrend_intercept_residual_with_1986-2009_and_2015-2099.png
peak memory: 525.73 MiB, increment: 239.46 MiB
CPU times: user 1.17 s, sys: 1.51 s, total: 2.68 s
Wall time: 1.3 s

Plot test with triangular shape

In [16]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot test with triangular shape')

from matplotlib.patches import Polygon

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_intercept_residual.pckl'
with open(savefile, 'rb') as file:
    data2plot = pickle.load(file)

#
watermass='southern and atlantic significant'

#____________________
# Set figname for saving figure

figname = dirout + 'barplot_AOUtrend_OURxAgetrend_intercept_residual.png'

#--------------------
# Figure parameters
#--------------------

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']

zfact = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC

label_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    'intercept'  : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    'residual'   : r'residual'
}

color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'intercept' : cmcrameri.cm.batlowS.colors[1],
    'residual'  : cmcrameri.cm.batlowS.colors[2]
}

kwbar = dict(linewidth=0.5, align='center')
kwbar1 = dict(closed=True, linewidth=.5, edgecolor='k')

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

ncol = 3
nrow = 2
fwidth_cm = 6
fheight_cm = 5
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize)

#--------------------
# Plot
#--------------------

for iesm, vesm in enumerate(esm_list): 
    
    zax = ax.flat[iesm]

    #--------------------
    # 50 year periods
    #--------------------

    number = '1'

    # Get x-ticks and xxx and www0 and www1
    x_ticks = [int(sss[:4]) for sss in data2plot[vesm][number]['period']]
    x_ticks.append(int(data2plot[vesm][number]['period'][-1][-4:])+1)
    xxx = (np.array(x_ticks[:-1])+np.array(x_ticks[1:]))/2
    
    www0 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.95
    www1 = (np.array(x_ticks[1:]) - np.array(x_ticks[:-1])) * 0.7
    
    
    #_________________
    # ∆AOU
    
    var = '∆AOU'
    
    hhh = np.array(data2plot[vesm][number][var]['total']) * zfact
    zax.bar(xxx, hhh, width=www0, label='Global ∆AOU', color=color_dict['Global '+var], edgecolor=color_dict['Global '+var], **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep']) * zfact
    # zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

    # hhh = np.array(data2plot[vesm][number][var]['deep significant']) * zfact
    # zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

    hhh = np.array(data2plot[vesm][number][var][watermass]) * zfact
    zax.bar(xxx, hhh, width=www1, label=var, color=color_dict[var], edgecolor=color_dict[var], **kwbar)

    nnn = 3
    www2=www1/(nnn+1)

    var = '∆Age'
    hhh = 0*np.array(data2plot[vesm][number][var][watermass])
    bbb = 0*hhh
    
    #_________________
    # ∆Age
    iii = 0
    var = '∆Age'
    hhh += np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    for ihhh, vhhh in enumerate(hhh): 
        height = vhhh - bbb[ihhh]
        p0 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]             ]]
        p1 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]             ]]
        p2 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        p3 = [[xxx1[ihhh]               , vhhh                  ]]
        p4 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        vertices = p0 + p1 + p2 + p3 + p4
        if ihhh==0: polygon = Polygon(vertices, facecolor=color_dict[var], label=var, **kwbar1)
        else: polygon = Polygon(vertices, facecolor=color_dict[var], **kwbar1)
        zax.add_patch(polygon)
        # Add horizontal line at the top of the triangle
        zax.plot([xxx1[ihhh] - www2[ihhh]/2, xxx1[ihhh] + www2[ihhh]/2], [vhhh, vhhh], lw=.5, color='black')
    #    

    bbb=np.copy(hhh)
    
    #_________________
    # intercept
    iii = 1
    var = 'intercept'
    hhh += np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    for ihhh, vhhh in enumerate(hhh): 
        height = vhhh - bbb[ihhh]
        p0 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]             ]]
        p1 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]             ]]
        p2 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        p3 = [[xxx1[ihhh]               , vhhh                  ]]
        p4 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        vertices = p0 + p1 + p2 + p3 + p4
        if ihhh==0: polygon = Polygon(vertices, facecolor=color_dict[var], label=var, **kwbar1)
        else: polygon = Polygon(vertices, facecolor=color_dict[var], **kwbar1)
        zax.add_patch(polygon)
        # Add horizontal line at the top of the triangle
        zax.plot([xxx1[ihhh] - www2[ihhh]/2, xxx1[ihhh] + www2[ihhh]/2], [vhhh, vhhh], lw=.5, color='black')
    #    
    
    bbb=np.copy(hhh)
    

    #_________________
    # residual
    iii = 2
    var = 'residual'
    hhh += np.array(data2plot[vesm][number][var][watermass]) * zfact
    xxx1 = xxx-(nnn-(1+2*iii))*www2/2
    for ihhh, vhhh in enumerate(hhh): 
        height = vhhh - bbb[ihhh]
        p0 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]             ]]
        p1 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]             ]]
        p2 = [[xxx1[ihhh] + www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        p3 = [[xxx1[ihhh]               , vhhh                  ]]
        p4 = [[xxx1[ihhh] - www2[ihhh]/2, bbb[ihhh]+ 0.7*height ]]
        vertices = p0 + p1 + p2 + p3 + p4
        if ihhh==0: polygon = Polygon(vertices, facecolor=color_dict[var], label=var, **kwbar1)
        else: polygon = Polygon(vertices, facecolor=color_dict[var], **kwbar1)
        zax.add_patch(polygon)
        # Add horizontal line at the top of the triangle
        zax.plot([xxx1[ihhh] - www2[ihhh]/2, xxx1[ihhh] + www2[ihhh]/2], [vhhh, vhhh], lw=.5, color='black')
    #    
    
    
    #_________________
    # Decorate
    
    # if iesm==0: zax.legend()
    
    ttl = alphabet_reverse.pop()+') '+esm_name_dict[vesm]
    zax.set_title(ttl, loc='left')
    zax.set_xticks(x_ticks)
    ylim = zax.get_ylim()
    dylim = 0.1*(ylim[1]-ylim[0])
    ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
    zax.set_ylim(ylim2)
    zax.axhline(0, c='k', lw=.5)

    #_________________
    # Add R2 text
    iii=0
    for ind, val in enumerate(data2plot[vesm][number]['R2'][watermass]): 
        # xtext = xxx[ind]-(nnn-(1+2*iii))*www2/2-www2/2
        # ytext = np.max([data2plot[vesm][number]['∆AOU'    ][watermass][ind],  data2plot[vesm][number]['∆Age'     ][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind], \
        #                 data2plot[vesm][number]['∆Age'    ][watermass][ind] + data2plot[vesm][number]['intercept'][watermass][ind] + \
        #                 data2plot[vesm][number]['residual'][watermass][ind], 0])
        # zax.text( xtext, ytext*zfact, f'{val:.2f}')
        xtext = xxx[ind]
        ytext = ylim2[0]+0.01*(ylim2[1]-ylim2[0])
        zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
    #
    xlim = zax.get_xlim()
    xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
    zax.text(xtext, ytext, r'$R^2$:', ha='left', va='bottom', size=6)

#


fig.tight_layout()

#--------------------
# Decorate
#--------------------

ax.flat[-1].axis('off')

#____________________
# legend
    # if iesm==0: zax.legend()
zax=ax.flat[0]
aaa=zax.get_legend_handles_labels()
handles = aaa[0][-2:]+aaa[0][:3]
labels = [label_dict[vvv] for vvv in aaa[1]]
labels = labels[-2:] + labels[:3]
zax = ax.flat[-1]
zax.legend(handles, labels, frameon=False, handletextpad=1, loc='upper left', bbox_to_anchor=(0, 1))

#____________________
# axis labels
for zax in ax[: , 0]: zax.set_ylabel(r'[Pg$\,$C yr$^{-1}$]')

#--------------------
# Save figure
#--------------------

# print('Save figure...')
# fig.savefig(figname, bbox_inches='tight')
# print('Figure saved: '+figname)
2025-03-11 10:13:24.314708
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot test with triangular shape
peak memory: 594.32 MiB, increment: 270.62 MiB
CPU times: user 612 ms, sys: 1.27 s, total: 1.88 s
Wall time: 699 ms

Figure ∆AOU = OUR * ∆Age + reminder

Prepare data2plot

In [45]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Figure ∆AOU = OUR * ∆Age + reminder')
print('## Prepare data2plot')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
tperiod = '2015-2099'


#----------------
# Define some functions
#----------------
def compute_xavg(zwnp, zwvolume): 
    zwda = xr.DataArray(zwnp, dims=zwvolume.dims, coords=zwvolume.coords)
    zwda_xavg = zonal_avg(zwda, zwvolume)
    xavg_dict = {'xxx': zwda_xavg.latitude.values, 'yyy': zwda_xavg.depth.values, 'ccc': zwda_xavg.values}
    return xavg_dict
#

#----------------
# Initiate data2plot
#----------------

data2plot = {
    'ESM': [], \
    'Global': {
        '∆AOU': {'total':[], 'deep':[], 'deep significant':[], \
                 'southern and atlantic significant': [], \
                 'southern and atlantic': []}, \
        '∆Age': {'southern and atlantic significant': [], \
                 'southern and atlantic': []}, \
        'reminder': {'southern and atlantic significant': [], \
                     'southern and atlantic': []}, \
        'R2': {'southern and atlantic significant': [], \
               'southern and atlantic': []}, \
        'volume': {'total':[], 'deep':[], 'deep significant':[], \
                   'southern and atlantic significant': [], \
                   'southern and atlantic': []}
    }, \
    'xavg': {
        '∆AOU': {'total':[], 'deep':[], 'deep significant':[], \
                 'southern and atlantic significant': [], \
                 'southern and atlantic': []}, \
        '∆Age': {'southern and atlantic significant': [], \
                 'southern and atlantic': []}, \
        'reminder': {'southern and atlantic significant': [], \
                     'southern and atlantic': []}        
    }
}

#----------------
# Loop on ESM
#----------------


for iesm, vesm in enumerate(esm_list): 
    
    print('-----------------------')
    print('Prepare data for %s...'%vesm)
    print('-----------------------')

    data2plot['ESM'].append(vesm)
    
    #----------------
    # Load data
    #----------------
    print('   Load data...')

    if tperiod=='2015-2099': simu = 'ssp585'
    elif tperiod=='1986-2009': simu = 'historical'

    savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
    with open(savefile, 'rb') as file:
        data2process_significant = pickle.load(file)
    #
    savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'_include_low_significant_trend.pckl'
    with open(savefile, 'rb') as file:
        data2process_with_low_significant = pickle.load(file)
    #

    #----------------
    # Compute volume and omask
    #----------------
    print('   Compute volume and omask...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    fname = netcdfdir+f'{vesm}_historical_sigma0_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_sigma0 = zwds['sigma0']
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda_sigma0 = zwda_sigma0.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda_sigma0.depth)

    omask = ~(zwda_sigma0.isnull()).rename('mask').drop(['latitude', 'longitude'])
    volume = omask * depthickness * zarea

    del depbounds, depthickness
    
    #----------------
    # Southern and atlantic significant
    #----------------
    print('   Southern and atlantic significant...')

    zw_delta_aou_SA, zw_delta_age_SA, zw_reminder_SA = 0, 0, 0
    zwvolume_SA, zwr2_SA = 0, 0
    zw_delta_aou_3D_SA, zw_delta_age_3D_SA, zw_reminder_3D_SA = [], [], []
    for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 

        #______________
        # Global
        zwaou    = data2process_significant[vesm][watermass]['integrated aou trend']
        zwage    = data2process_significant[vesm][watermass]['integrated age trend']
        zwour    = data2process_significant[vesm][watermass]['linregress']['slope']
        zwr2     = data2process_significant[vesm][watermass]['linregress']['rvalue']**2
        zwvolume = data2process_significant[vesm][watermass]['volume']

        zw_delta_age = zwour * zwage
        zw_reminder = zwaou - zw_delta_age

        zw_delta_aou_SA += zwaou
        zw_delta_age_SA += zw_delta_age
        zw_reminder_SA  += zw_reminder

        zwr2_SA     += zwr2 * zwvolume
        zwvolume_SA += zwvolume
        
        #______________
        # XAVG
        zw_pred_3D = data2process_significant[vesm][watermass]['aou prediction']['predicted']['3D']['ccc']
        zw_bias_3D = data2process_significant[vesm][watermass]['aou prediction']['error']['3D']['ccc']
        zw_itcpt   = data2process_significant[vesm][watermass]['linregress']['intercept']

        zw_delta_aou_3D = zw_pred_3D + zw_bias_3D
        zw_delta_age_3D = zw_pred_3D - zw_itcpt
        zw_reminder_3D  = zw_bias_3D + zw_itcpt
        
        zw_delta_aou_3D_SA.append(zw_delta_aou_3D)
        zw_delta_age_3D_SA.append(zw_delta_age_3D)
        zw_reminder_3D_SA .append(zw_reminder_3D )


    #
    zwr2_SA = zwr2_SA / zwvolume_SA

    zw_delta_aou_3D_SA = np.array(zw_delta_aou_3D_SA)
    zw_delta_age_3D_SA = np.array(zw_delta_age_3D_SA)
    zw_reminder_3D_SA  = np.array(zw_reminder_3D_SA )

    all_nan_mask = np.all(np.isnan(zw_delta_aou_3D_SA), axis=0)
    zwnansum = np.nansum(zw_delta_aou_3D_SA, axis=0)
    zw_delta_aou_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)

    all_nan_mask = np.all(np.isnan(zw_delta_age_3D_SA), axis=0)
    zwnansum = np.nansum(zw_delta_age_3D_SA, axis=0)
    zw_delta_age_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)

    all_nan_mask = np.all(np.isnan(zw_reminder_3D_SA), axis=0)
    zwnansum = np.nansum(zw_reminder_3D_SA, axis=0)
    zw_reminder_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)
        
    watermass = 'southern and atlantic significant'
    #______________
    # Global
    datatype = 'Global'
    data2plot[datatype]['∆AOU'     ][watermass].append(zw_delta_aou_SA)
    data2plot[datatype]['∆Age'     ][watermass].append(zw_delta_age_SA)
    data2plot[datatype]['reminder' ][watermass].append(zw_reminder_SA)
    data2plot[datatype]['R2'       ][watermass].append(zwr2_SA)
    data2plot[datatype]['volume'   ][watermass].append(zwvolume_SA)
    #______________
    # XAVG
    datatype = 'xavg'
    xavg = compute_xavg(zw_delta_aou_3D_SA, volume)
    data2plot[datatype]['∆AOU'][watermass].append(xavg)
    xavg = compute_xavg(zw_delta_age_3D_SA, volume)
    data2plot[datatype]['∆Age'][watermass].append(xavg)
    xavg = compute_xavg(zw_reminder_3D_SA, volume)
    data2plot[datatype]['reminder'][watermass].append(xavg)

    #----------------
    # Southern and atlantic including low significant water-masses
    #----------------
    print('   Southern and atlantic including low significant water-masses...')
    
    zw_delta_aou_SA, zw_delta_age_SA, zw_reminder_SA = 0, 0, 0
    zwvolume_SA, zwr2_SA = 0, 0
    zw_delta_aou_3D_SA, zw_delta_age_3D_SA, zw_reminder_3D_SA = [], [], []
    for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 

        #______________
        # Global
        zwaou    = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
        zwage    = data2process_with_low_significant[vesm][watermass]['integrated age trend']
        zwour    = data2process_with_low_significant[vesm][watermass]['linregress']['slope']
        zwr2     = data2process_with_low_significant[vesm][watermass]['linregress']['rvalue']**2
        zwvolume = data2process_with_low_significant[vesm][watermass]['volume']

        zw_delta_age = zwour * zwage
        zw_reminder = zwaou - zw_delta_age

        zw_delta_aou_SA += zwaou
        zw_delta_age_SA += zw_delta_age
        zw_reminder_SA  += zw_reminder

        zwr2_SA     += zwr2 * zwvolume
        zwvolume_SA += zwvolume
        
        #______________
        # XAVG
        zw_pred_3D = data2process_with_low_significant[vesm][watermass]['aou prediction']['predicted']['3D']['ccc']
        zw_bias_3D = data2process_with_low_significant[vesm][watermass]['aou prediction']['error']['3D']['ccc']
        zw_itcpt   = data2process_with_low_significant[vesm][watermass]['linregress']['intercept']

        zw_delta_aou_3D = zw_pred_3D + zw_bias_3D
        zw_delta_age_3D = zw_pred_3D - zw_itcpt
        zw_reminder_3D  = zw_bias_3D + zw_itcpt
        
        zw_delta_aou_3D_SA.append(zw_delta_aou_3D)
        zw_delta_age_3D_SA.append(zw_delta_age_3D)
        zw_reminder_3D_SA .append(zw_reminder_3D )


    #
    zwr2_SA = zwr2_SA / zwvolume_SA

    zw_delta_aou_3D_SA = np.array(zw_delta_aou_3D_SA)
    zw_delta_age_3D_SA = np.array(zw_delta_age_3D_SA)
    zw_reminder_3D_SA  = np.array(zw_reminder_3D_SA )

    all_nan_mask = np.all(np.isnan(zw_delta_aou_3D_SA), axis=0)
    zwnansum = np.nansum(zw_delta_aou_3D_SA, axis=0)
    zw_delta_aou_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)

    all_nan_mask = np.all(np.isnan(zw_delta_age_3D_SA), axis=0)
    zwnansum = np.nansum(zw_delta_age_3D_SA, axis=0)
    zw_delta_age_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)

    all_nan_mask = np.all(np.isnan(zw_reminder_3D_SA), axis=0)
    zwnansum = np.nansum(zw_reminder_3D_SA, axis=0)
    zw_reminder_3D_SA = np.where(all_nan_mask, np.nan, zwnansum)
        
    watermass = 'southern and atlantic'
    #______________
    # Global
    datatype = 'Global'
    data2plot[datatype]['∆AOU'     ][watermass].append(zw_delta_aou_SA)
    data2plot[datatype]['∆Age'     ][watermass].append(zw_delta_age_SA)
    data2plot[datatype]['reminder' ][watermass].append(zw_reminder_SA)
    data2plot[datatype]['R2'       ][watermass].append(zwr2_SA)
    data2plot[datatype]['volume'   ][watermass].append(zwvolume_SA)
    #______________
    # XAVG
    datatype = 'xavg'
    xavg = compute_xavg(zw_delta_aou_3D_SA, volume)
    data2plot[datatype]['∆AOU'][watermass].append(xavg)
    xavg = compute_xavg(zw_delta_age_3D_SA, volume)
    data2plot[datatype]['∆Age'][watermass].append(xavg)
    xavg = compute_xavg(zw_reminder_3D_SA, volume)
    data2plot[datatype]['reminder'][watermass].append(xavg)
       
    #----------------
    # Total, deep and deep significant (only ∆AOU and volume)...
    #----------------
    print('   Total, deep and deep significant (only ∆AOU and volume)...')
    
    datatype = 'Global'
    
    # total
    watermass = 'total'
    zw_delta_aou = data2process_significant[vesm][watermass]['integrated aou trend']
    data2plot[datatype]['∆AOU'     ][watermass].append(zw_delta_aou)
    zwvolume     = data2process_significant[vesm][watermass]['volume']
    data2plot[datatype]['volume'     ][watermass].append(zwvolume)
    
    # deep significant
    watermass = 'deep'
    zw_delta_aou = data2process_significant[vesm][watermass]['integrated aou trend']
    data2plot[datatype]['∆AOU'     ][watermass+' significant'].append(zw_delta_aou)
    zwvolume     = data2process_significant[vesm][watermass]['volume']
    data2plot[datatype]['volume'     ][watermass+' significant'].append(zwvolume)
    
    # deep including low significance
    watermass = 'deep'
    zw_delta_aou = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
    data2plot[datatype]['∆AOU'     ][watermass].append(zw_delta_aou)
    zwvolume     = data2process_significant[vesm][watermass]['volume']
    data2plot[datatype]['volume'     ][watermass].append(zwvolume)

    #
#

#----------------
# Save data2plot in pickle
#----------------

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#
2025-03-11 11:55:18.195199
# Figure ∆AOU = OUR * ∆Age + reminder
## Prepare data2plot
-----------------------
Prepare data for MPI-ESM1-2-LR...
-----------------------
   Load data...
   Compute volume and omask...
   Southern and atlantic significant...
   Southern and atlantic including low significant water-masses...
   Total, deep and deep significant (only ∆AOU and volume)...
-----------------------
Prepare data for ACCESS-ESM1-5...
-----------------------
   Load data...
   Compute volume and omask...
   Southern and atlantic significant...
   Southern and atlantic including low significant water-masses...
   Total, deep and deep significant (only ∆AOU and volume)...
-----------------------
Prepare data for IPSL-CM6A-LR...
-----------------------
   Load data...
   Compute volume and omask...
   Southern and atlantic significant...
   Southern and atlantic including low significant water-masses...
   Total, deep and deep significant (only ∆AOU and volume)...
-----------------------
Prepare data for MIROC-ES2L...
-----------------------
   Load data...
   Compute volume and omask...
   Southern and atlantic significant...
   Southern and atlantic including low significant water-masses...
   Total, deep and deep significant (only ∆AOU and volume)...
-----------------------
Prepare data for NorESM2-LM...
-----------------------
   Load data...
   Compute volume and omask...
   Southern and atlantic significant...
   Southern and atlantic including low significant water-masses...
   Total, deep and deep significant (only ∆AOU and volume)...
peak memory: 53140.05 MiB, increment: 30708.93 MiB
CPU times: user 14.8 s, sys: 25 s, total: 39.9 s
Wall time: 52 s

Plot

In [83]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot')

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder.pckl'
with open(savefile, 'rb') as file:
    data2plot = pickle.load(file)

#
watermass='southern and atlantic significant'

#____________________
# Set figname for saving figure

figname = dirout + 'plot_AOUtrend_OURxAgetrend_reminder.png'

#--------------------
# Figure parameters
#--------------------

# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
esm_list = data2plot['ESM']
section_list = ['∆AOU', '∆Age', 'reminder']

zfact_glob = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC
zfact_section = 1

#______________
# Labels
labels_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    'reminder'   : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    # 'reminder'  : r'Reminder',
}

#______________
# barplot colors
barplot_color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'reminder' : cmcrameri.cm.batlowS.colors[1]
}

#______________
# barplot kwargs
kwbar = dict(linewidth=0.5, align='center')
kwbar1 = dict(linewidth=.5, align='center', edgecolor='k')

#______________
# Sections axis labeling and limits
xlabel = 'Latitude [Deg. N]'
ylabel = 'Depth [m]'
xlim = (-90, 90)
ylim = (6000, -10)

#______________
# Section cmap
a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# Section pcm kwargs
kwpcm = dict(cmap=newcmap, vmin=-.2, vmax=.2)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

# Pop one now to save a) for the barplot at the top
extended_alphabet_reverse.pop()

#______________
# Set figure
ncol = 3
nrow = len(esm_list)
fwidth_cm = 4
fheight_cm = 3
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, sharex='col', sharey='row')

#--------------------
# Plot sections
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    
    for iii, vvv in enumerate(section_list): 

        zwdata = data2plot['xavg'][vvv][watermass][iesm]

        zax = ax[iesm, iii]
        xxx = zwdata['xxx']
        yyy = zwdata['yyy']
        ccc = zwdata['ccc'] * zfact_section
        # ccc = np.where(ccc!=0, ccc, np.nan)
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        pcm[iesm, iii] = zwpcm
        zax.set_ylim(ylim)
        zax.set_xlim(xlim)

        zwtxt = extended_alphabet_reverse.pop() + f') '
        zax.text(.02, .98, zwtxt, transform=zax.transAxes, ha='left', va='top')
    #
    
    
#

#--------------------
# Decorate sections
#--------------------

fig.tight_layout()

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.2, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#

for zax in ax[:, 0]: zax.set_ylabel(ylabel)
for zax in ax[-1, :]: zax.set_xlabel(xlabel)
        
for iii, vvv in enumerate(section_list): 
    zax = ax[0, iii]
    zax.text(.5, 1.2, labels_dict[vvv], transform=zax.transAxes, ha='center', va='bottom')
#

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, -1].get_position()
nx0 = zw1.x0 + .5*zw1.width
ny0 = zw2.y0 - .8*zw1.height
nw  = zw2.x0 + .5*zw2.width - nx0
nh  = 0.05*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
clabel = '[mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]'
cbar.set_label(clabel)

#--------------------
# Add barplot
#--------------------

zw1 = ax[0, 0].get_position()
zw2 = ax[0, -1].get_position()
nx0 = zw1.x0
ny0 = zw1.y1 + .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 1.5*zw1.height
zax = fig.add_axes([nx0, ny0, nw, nh])

#_________________
# Get x-ticks and xxx and www0 and www1
xxx = np.arange(len(esm_list))

www0 =  0.95
www1 =  0.7

#_________________
# ∆AOU

var = '∆AOU'

hhh = np.array(data2plot['Global'][var]['total']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Global ∆AOU', color=barplot_color_dict['Global '+var], edgecolor=barplot_color_dict['Global '+var], **kwbar)

hhh = np.array(data2plot['Global'][var]['deep']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

# hhh = np.array(data2plot['Global'][var]['deep significant']) * zfact_glob
# zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
zax.bar(xxx, hhh, width=www1, label=var, color=barplot_color_dict[var], edgecolor=barplot_color_dict[var], **kwbar)

nnn = 2
www2=www1/(nnn+1)

#_________________
# ∆Age
iii = 0
var = '∆Age'
hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
bbb = 0*hhh
xxx1 = xxx-(nnn-(1+2*iii))*www2/2
bars = zax.bar(xxx1, hhh, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)

#_________________
# Reminder
iii = 1
var = 'reminder'
bbb += hhh
hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
xxx2 = xxx-(nnn-(1+2*iii))*www2/2
zax.bar(xxx2, hhh, bottom=bbb, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)


#--------------------
# Decorate barplot
#--------------------

zax.axhline(0, c='k', lw=.5)

#____________________
# Set labels and title

ttl = 'a) Spatially integrated trends'
zax.set_title(ttl, loc='left')
zax.set_ylabel(r'[Pg$\,$C yr$^{-1}$]')
x_tick_labels = [esm_name_dict[vesm] for vesm in esm_list]
zax.set_xticks(xxx)
zax.set_xticklabels(x_tick_labels)

#____________________
# Adjust axis limits

ylim = zax.get_ylim()
dylim = 0.30*(ylim[1]-ylim[0])
ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
zax.set_ylim(ylim2)

xlim = zax.get_xlim()
dxlim = 0.05*(xlim[1]-xlim[0])
xlim2 = (xlim[0]-dxlim, xlim[1])
zax.set_xlim(xlim2)


#____________________
# legend
aaa=zax.get_legend_handles_labels()
handles = aaa[0]
labels = [labels_dict[vvv] for vvv in aaa[1]]
zax.legend(handles, labels, frameon=False, handletextpad=1, ncol=2,\
           loc='upper left', bbox_to_anchor=(0, 1))

#--------------------
# Add R2 text
#--------------------
ylim = zax.get_ylim()
xlim = zax.get_xlim()

iii=0
ytext = ylim[0]+0.005*(ylim[1]-ylim[0])
for ind, val in enumerate(data2plot['Global']['R2'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'$\overline{R^2}$:', ha='left', va='bottom', size=6)

#--------------------
# Add volume text
#--------------------
iii=0
ytext = ylim[0]+0.1*(ylim[1]-ylim[0])
totvolume = data2plot['Global']['volume']['total']
for ind, val in enumerate(data2plot['Global']['volume'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val/totvolume[ind]*100:.0f}%', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'Vol. ratio:', ha='left', va='bottom', size=6)

#--------------------
# Add share of dAge
#--------------------
iii=0
ytext = 0.005*(ylim[1]-ylim[0])
daou = data2plot['Global']['∆AOU'][watermass]
for ind, val in enumerate(data2plot['Global']['∆Age'][watermass]): 
    xtext = xxx1[ind]
    zax.text( xtext, ytext, f'{val/daou[ind]*100:.0f}%', c='white', ha='center', va='bottom', size=6)
#

#--------------------
# Print share of dAge
#--------------------
print('---- dAGE/dAOU ----')
daou = data2plot['Global']['∆AOU'][watermass]
dage = data2plot['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot['ESM']): 
    print(f'{val}: {dage[ind]/daou[ind]*100:.0f}%')
#
print('-----------------')

#--------------------
# Print dAOU, dAge
#--------------------
print('---- dAGE, dAOU ----')
daou = data2plot['Global']['∆AOU'][watermass]
dage = data2plot['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot['ESM']): 
    print(f'{val}: {dage[ind]*zfact_glob:.2f}, {daou[ind]*zfact_glob:.2f} [PgC/yr]')
#
print('-----------------')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-25 17:45:29.602986
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot
<string>:115: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
---- dAGE/dAOU ----
MPI-ESM1-2-LR: 59%
ACCESS-ESM1-5: 38%
IPSL-CM6A-LR: 96%
MIROC-ES2L: 79%
NorESM2-LM: 78%
-----------------
---- dAGE, dAOU ----
MPI-ESM1-2-LR: 0.16, 0.28 [PgC/yr]
ACCESS-ESM1-5: 0.13, 0.35 [PgC/yr]
IPSL-CM6A-LR: 0.32, 0.33 [PgC/yr]
MIROC-ES2L: 0.50, 0.63 [PgC/yr]
NorESM2-LM: 0.32, 0.41 [PgC/yr]
-----------------
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/plot_AOUtrend_OURxAgetrend_reminder.png
peak memory: 56155.16 MiB, increment: 28015.85 MiB
CPU times: user 2.85 s, sys: 1.03 s, total: 3.88 s
Wall time: 3.09 s

Plot contribution relative to ∆AOU

In [45]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot contribution relative to ∆AOU')

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder.pckl'
with open(savefile, 'rb') as file:
    data2plot = pickle.load(file)

#
watermass='southern and atlantic significant'

#____________________
# Set figname for saving figure

figname = dirout + 'plot_AOUtrend_OURxAgetrend_reminder_relative_contributions.png'

#--------------------
# Figure parameters
#--------------------

# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
esm_list = data2plot['ESM']
section_list = ['∆AOU', '∆Age', 'reminder']

zfact_glob = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC
zfact_section = 1

#______________
# Labels
labels_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    'reminder'   : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    # 'reminder'  : r'Reminder',
}

#______________
# barplot colors
barplot_color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'reminder' : cmcrameri.cm.batlowS.colors[1]
}

#______________
# barplot kwargs
kwbar = dict(linewidth=0.5, align='center')
kwbar1 = dict(linewidth=.5, align='center', edgecolor='k')

#______________
# Sections axis labeling and limits
xlabel = 'Latitude [Deg. N]'
ylabel = 'Depth [m]'
xlim = (-90, 90)
ylim = (6000, -10)

#______________
# Section cmap
a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# Section pcm kwargs
kwpcm = dict(cmap=newcmap, vmin=-1.5, vmax=1.5)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

# Pop one now to save a) for the barplot at the top
extended_alphabet_reverse.pop()

#______________
# Set figure
ncol = 3
nrow = len(esm_list)
fwidth_cm = 4
fheight_cm = 3
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, sharex='col', sharey='row')

#--------------------
# Plot sections
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
        
    ccc_ref = data2plot['xavg']['∆AOU'][watermass][iesm]['ccc']
    
    for iii, vvv in enumerate(section_list): 

        zwdata = data2plot['xavg'][vvv][watermass][iesm]

        zax = ax[iesm, iii]
        xxx = zwdata['xxx']
        yyy = zwdata['yyy']
        ccc = zwdata['ccc'] / ccc_ref
        # ccc = np.where(ccc!=0, ccc, np.nan)
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        pcm[iesm, iii] = zwpcm
        zax.set_ylim(ylim)
        zax.set_xlim(xlim)

        zwtxt = extended_alphabet_reverse.pop() + f') '
        zax.text(.02, .98, zwtxt, transform=zax.transAxes, ha='left', va='top')

    #
#

#--------------------
# Decorate sections
#--------------------

fig.tight_layout()

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.2, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#

for zax in ax[:, 0]: zax.set_ylabel(ylabel)
for zax in ax[-1, :]: zax.set_xlabel(xlabel)
        
for iii, vvv in enumerate(section_list): 
    zax = ax[0, iii]
    zax.text(.5, 1.2, labels_dict[vvv], transform=zax.transAxes, ha='center', va='bottom')
#

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, -1].get_position()
nx0 = zw1.x0 + .5*zw1.width
ny0 = zw2.y0 - .8*zw1.height
nw  = zw2.x0 + .5*zw2.width - nx0
nh  = 0.05*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
clabel = 'ratio to ∆AOU'
cbar.set_label(clabel)

#--------------------
# Add barplot
#--------------------

zw1 = ax[0, 0].get_position()
zw2 = ax[0, -1].get_position()
nx0 = zw1.x0
ny0 = zw1.y1 + .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 1.5*zw1.height
zax = fig.add_axes([nx0, ny0, nw, nh])

#_________________
# Get x-ticks and xxx and www0 and www1
xxx = np.arange(len(esm_list))

www0 =  0.95
www1 =  0.7

#_________________
# ∆AOU

var = '∆AOU'

hhh = np.array(data2plot['Global'][var]['total']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Global ∆AOU', color=barplot_color_dict['Global '+var], edgecolor=barplot_color_dict['Global '+var], **kwbar)

hhh = np.array(data2plot['Global'][var]['deep']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

# hhh = np.array(data2plot['Global'][var]['deep significant']) * zfact_glob
# zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
zax.bar(xxx, hhh, width=www1, label=var, color=barplot_color_dict[var], edgecolor=barplot_color_dict[var], **kwbar)

nnn = 2
www2=www1/(nnn+1)

#_________________
# ∆Age
iii = 0
var = '∆Age'
hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
bbb = 0*hhh
xxx1 = xxx-(nnn-(1+2*iii))*www2/2
bars = zax.bar(xxx1, hhh, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)

#_________________
# Reminder
iii = 1
var = 'reminder'
bbb += hhh
hhh = np.array(data2plot['Global'][var][watermass]) * zfact_glob
xxx1 = xxx-(nnn-(1+2*iii))*www2/2
zax.bar(xxx1, hhh, bottom=bbb, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)


#--------------------
# Decorate barplot
#--------------------

zax.axhline(0, c='k', lw=.5)

#____________________
# Set labels and title

ttl = 'a) Spatially integrated trends'
zax.set_title(ttl, loc='left')
zax.set_ylabel(r'[Pg$\,$C yr$^{-1}$]')
x_tick_labels = [esm_name_dict[vesm] for vesm in esm_list]
zax.set_xticks(xxx)
zax.set_xticklabels(x_tick_labels)

#____________________
# Adjust axis limits

ylim = zax.get_ylim()
dylim = 0.30*(ylim[1]-ylim[0])
ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
zax.set_ylim(ylim2)

xlim = zax.get_xlim()
dxlim = 0.05*(xlim[1]-xlim[0])
xlim2 = (xlim[0]-dxlim, xlim[1])
zax.set_xlim(xlim2)


#____________________
# legend
aaa=zax.get_legend_handles_labels()
handles = aaa[0]
labels = [labels_dict[vvv] for vvv in aaa[1]]
zax.legend(handles, labels, frameon=False, handletextpad=1, ncol=2,\
           loc='upper left', bbox_to_anchor=(0, 1))

#--------------------
# Add R2 text
#--------------------
ylim = zax.get_ylim()
xlim = zax.get_xlim()

iii=0
ytext = ylim[0]+0.005*(ylim[1]-ylim[0])
for ind, val in enumerate(data2plot['Global']['R2'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'$\overline{R^2}$:', ha='left', va='bottom', size=6)

#--------------------
# Add volume text
#--------------------
iii=0
ytext = ylim[0]+0.1*(ylim[1]-ylim[0])
totvolume = data2plot['Global']['volume']['total']
for ind, val in enumerate(data2plot['Global']['volume'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val/totvolume[ind]*100:.0f}%', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'Vol. ratio:', ha='left', va='bottom', size=6)

#--------------------
# Print share of dAge
#--------------------
print('---- dAGE/dAOU ----')
daou = data2plot['Global']['∆AOU'][watermass]
dage = data2plot['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot['ESM']): 
    print(f'{val}: {dage[ind]/daou[ind]*100:.0f}%')
#
print('-----------------')

#--------------------
# Print dAOU, dAge
#--------------------
print('---- dAGE, dAOU ----')
daou = data2plot['Global']['∆AOU'][watermass]
dage = data2plot['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot['ESM']): 
    print(f'{val}: {dage[ind]*zfact_glob:.2f}, {daou[ind]*zfact_glob:.2f} [PgC/yr]')
#
print('-----------------')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-18 16:53:40.152439
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot
<string>:117: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
---- dAGE/dAOU ----
MPI-ESM1-2-LR: 59%
ACCESS-ESM1-5: 38%
IPSL-CM6A-LR: 96%
MIROC-ES2L: 79%
NorESM2-LM: 78%
-----------------
---- dAGE, dAOU ----
MPI-ESM1-2-LR: 0.16, 0.28 [PgC/yr]
ACCESS-ESM1-5: 0.13, 0.35 [PgC/yr]
IPSL-CM6A-LR: 0.32, 0.33 [PgC/yr]
MIROC-ES2L: 0.50, 0.63 [PgC/yr]
NorESM2-LM: 0.32, 0.41 [PgC/yr]
-----------------
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/plot_AOUtrend_OURxAgetrend_reminder_relative_contributions.png
peak memory: 65867.73 MiB, increment: 32882.03 MiB
CPU times: user 2.42 s, sys: 1.03 s, total: 3.45 s
Wall time: 2.53 s

Plot each watermasses

Prepare data2plot

In [37]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Figure ∆AOU = OUR * ∆Age + reminder')
print('## Plot each watermass')
print('### Prepare data2plot')

esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
tperiod = '2015-2099'


#----------------
# Define some functions
#----------------
def compute_xavg(zwnp, zwvolume): 
    zwda = xr.DataArray(zwnp, dims=zwvolume.dims, coords=zwvolume.coords)
    zwda_xavg = zonal_avg(zwda, zwvolume)
    xavg_dict = {'xxx': zwda_xavg.latitude.values, 'yyy': zwda_xavg.depth.values, 'ccc': zwda_xavg.values}
    return xavg_dict
#

#----------------
# Initiate data2plot
#----------------

data2plot = {
    'ESM': [], \
    'Global': {
        '∆AOU': {'southern dense significant':[], 'southern light significant':[], \
                 'atlantic dense significant':[], 'atlantic light significant':[], \
                 'southern dense':[], 'southern light':[], \
                 'atlantic dense':[], 'atlantic light':[]}, \
        '∆Age': {'southern dense significant':[], 'southern light significant':[], \
                 'atlantic dense significant':[], 'atlantic light significant':[], \
                 'southern dense':[], 'southern light':[], \
                 'atlantic dense':[], 'atlantic light':[]}, \
        'reminder': {'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[]}, \
        'R2': {'southern dense significant':[], 'southern light significant':[], \
               'atlantic dense significant':[], 'atlantic light significant':[], \
               'southern dense':[], 'southern light':[], \
               'atlantic dense':[], 'atlantic light':[]}, \
        'volume': {'southern dense significant':[], 'southern light significant':[], \
                   'atlantic dense significant':[], 'atlantic light significant':[], \
                   'southern dense':[], 'southern light':[], \
                   'atlantic dense':[], 'atlantic light':[]}
    }, \
    'xavg': {
        '∆AOU': {'southern dense significant':[], 'southern light significant':[], \
                 'atlantic dense significant':[], 'atlantic light significant':[], \
                 'southern dense':[], 'southern light':[], \
                 'atlantic dense':[], 'atlantic light':[]}, \
        '∆Age': {'southern dense significant':[], 'southern light significant':[], \
                 'atlantic dense significant':[], 'atlantic light significant':[], \
                 'southern dense':[], 'southern light':[], \
                 'atlantic dense':[], 'atlantic light':[]}, \
        'reminder': {'southern dense significant':[], 'southern light significant':[], \
                     'atlantic dense significant':[], 'atlantic light significant':[], \
                     'southern dense':[], 'southern light':[], \
                     'atlantic dense':[], 'atlantic light':[]}
    }
}

#----------------
# Loop on ESM
#----------------


for iesm, vesm in enumerate(esm_list):
    
    print('-----------------------')
    print('Prepare data for %s...'%vesm)
    print('-----------------------')

    data2plot['ESM'].append(vesm)
    
    #----------------
    # Load data
    #----------------
    print('   Load data...')

    if tperiod=='2015-2099': simu = 'ssp585'
    elif tperiod=='1986-2009': simu = 'historical'

    savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'.pckl'
    with open(savefile, 'rb') as file:
        data2process_significant = pickle.load(file)
    #
    savefile = dirout + 'data2plot_aou_trend_vs_age_trend_'+simu+'_'+tperiod+'_include_low_significant_trend.pckl'
    with open(savefile, 'rb') as file:
        data2process_with_low_significant = pickle.load(file)
    #

    #----------------
    # Compute volume and omask
    #----------------
    print('   Compute volume and omask...')
    if vesm == 'NorESM2-LM': fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', diresgf='/mnt/reef-ns1002k-ns9034k/', variant='r1i1p1f1')
    else: fname_list = get_esgf_dataset_filepaths('areacello', vesm, 'historical', variant=variant_dict[vesm], 
                                                  version=version_dict['historical']['areacello'][vesm], grid=grid_dict['areacello'][vesm])
    zwds = xr.open_mfdataset(fname_list, **kwopenmfds)
    zwds2 = zwds['areacello'].to_dataset()
    zwds2 = rename_vars_dims_coords(zwds2, rename_dict)
    zwds2 = split_coords_dimensions(zwds2)
    zwds2 = shift_180_lon(zwds2)
    zarea = zwds2['areacello']
    # FOR MPI model, invert latitude dimension
    if vesm == 'MPI-ESM1-2-LR' : 
        zarea = zarea.isel(j=slice(None, None, -1))
    #

    fname = netcdfdir+f'{vesm}_historical_sigma0_average_on_1986-2009.nc'
    zwds = xr.open_mfdataset(fname, **kwopenmfds)
    zwda_sigma0 = zwds['sigma0']
    if vesm == 'MPI-ESM1-2-LR' : 
        zwda_sigma0 = zwda_sigma0.isel(j=slice(None, None, -1))
    #

    depbounds, depthickness = compute_vertical_bounds_thickness(zwda_sigma0.depth)

    omask = ~(zwda_sigma0.isnull()).rename('mask').drop(['latitude', 'longitude'])
    volume = omask * depthickness * zarea

    del depbounds, depthickness
    
    
    #----------------
    # Loop on water-masses
    #----------------

    for watermass in ['southern dense', 'southern light', 'atlantic dense', 'atlantic light']: 
        
        print(f'    {watermass}...')
        
        #________________
        # Significant water-masses

        # Global
        zw_delta_aou = data2process_significant[vesm][watermass]['integrated aou trend']
        zwage        = data2process_significant[vesm][watermass]['integrated age trend']
        zwour        = data2process_significant[vesm][watermass]['linregress']['slope']
        zwr2         = data2process_significant[vesm][watermass]['linregress']['rvalue']**2
        zwvolume     = data2process_significant[vesm][watermass]['volume']
            
        zw_delta_age = zwour * zwage
        zw_reminder = zw_delta_aou - zw_delta_age

        datatype = 'Global'
        data2plot[datatype]['∆AOU'     ][watermass+' significant'].append(zw_delta_aou)
        data2plot[datatype]['∆Age'     ][watermass+' significant'].append(zw_delta_age)
        data2plot[datatype]['reminder' ][watermass+' significant'].append(zw_reminder)
        data2plot[datatype]['R2'       ][watermass+' significant'].append(zwr2)
        data2plot[datatype]['volume'   ][watermass+' significant'].append(zwvolume)
                
        # xavg
        zw_pred_3D = data2process_significant[vesm][watermass]['aou prediction']['predicted']['3D']['ccc']
        zw_bias_3D = data2process_significant[vesm][watermass]['aou prediction']['error']['3D']['ccc']
        zw_itcpt   = data2process_significant[vesm][watermass]['linregress']['intercept']

        zw_delta_aou_3D = zw_pred_3D + zw_bias_3D
        zw_delta_age_3D = zw_pred_3D - zw_itcpt
        zw_reminder_3D  = zw_bias_3D + zw_itcpt

        datatype = 'xavg'
        xavg = compute_xavg(zw_delta_aou_3D, volume)
        data2plot[datatype]['∆AOU'][watermass+' significant'].append(xavg)
        xavg = compute_xavg(zw_delta_age_3D, volume)
        data2plot[datatype]['∆Age'][watermass+' significant'].append(xavg)
        xavg = compute_xavg(zw_reminder_3D, volume)
        data2plot[datatype]['reminder'][watermass+' significant'].append(xavg)
        
        #________________
        # Including low significant water-masses

        # Global
        zw_delta_aou = data2process_with_low_significant[vesm][watermass]['integrated aou trend']
        zwage        = data2process_with_low_significant[vesm][watermass]['integrated age trend']
        zwour        = data2process_with_low_significant[vesm][watermass]['linregress']['slope']
        zwr2         = data2process_with_low_significant[vesm][watermass]['linregress']['rvalue']**2
        zwvolume     = data2process_with_low_significant[vesm][watermass]['volume']
            
        zw_delta_age = zwour * zwage
        zw_reminder = zw_delta_aou - zw_delta_age

        datatype = 'Global'
        data2plot[datatype]['∆AOU'     ][watermass].append(zw_delta_aou)
        data2plot[datatype]['∆Age'     ][watermass].append(zw_delta_age)
        data2plot[datatype]['reminder' ][watermass].append(zw_reminder)
        data2plot[datatype]['R2'       ][watermass].append(zwr2)
        data2plot[datatype]['volume'   ][watermass].append(zwvolume)
                
        # xavg
        zw_pred_3D = data2process_significant[vesm][watermass]['aou prediction']['predicted']['3D']['ccc']
        zw_bias_3D = data2process_significant[vesm][watermass]['aou prediction']['error']['3D']['ccc']
        zw_itcpt   = data2process_significant[vesm][watermass]['linregress']['intercept']

        zw_delta_aou_3D = zw_pred_3D + zw_bias_3D
        zw_delta_age_3D = zw_pred_3D - zw_itcpt
        zw_reminder_3D  = zw_bias_3D + zw_itcpt

        datatype = 'xavg'
        xavg = compute_xavg(zw_delta_aou_3D, volume)
        data2plot[datatype]['∆AOU'][watermass].append(xavg)
        xavg = compute_xavg(zw_delta_age_3D, volume)
        data2plot[datatype]['∆Age'][watermass].append(xavg)
        xavg = compute_xavg(zw_reminder_3D, volume)
        data2plot[datatype]['reminder'][watermass].append(xavg)
    #
#

#----------------
# Save data2plot in pickle
#----------------

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder_each_watermass.pckl'
with open(savefile, "wb") as file:
    pickle.dump(data2plot, file)
#
2025-03-18 15:58:49.262552
# Figure ∆AOU = OUR * ∆Age + reminder
## Plot each watermass
### Prepare data2plot
-----------------------
Prepare data for MPI-ESM1-2-LR...
-----------------------
   Load data...
   Compute volume and omask...
    southern dense...
    southern light...
    atlantic dense...
    atlantic light...
-----------------------
Prepare data for ACCESS-ESM1-5...
-----------------------
   Load data...
   Compute volume and omask...
    southern dense...
    southern light...
    atlantic dense...
    atlantic light...
-----------------------
Prepare data for IPSL-CM6A-LR...
-----------------------
   Load data...
   Compute volume and omask...
    southern dense...
    southern light...
    atlantic dense...
    atlantic light...
-----------------------
Prepare data for MIROC-ES2L...
-----------------------
   Load data...
   Compute volume and omask...
    southern dense...
    southern light...
    atlantic dense...
    atlantic light...
-----------------------
Prepare data for NorESM2-LM...
-----------------------
   Load data...
   Compute volume and omask...
    southern dense...
    southern light...
    atlantic dense...
    atlantic light...
peak memory: 60933.30 MiB, increment: 40967.80 MiB
CPU times: user 1min 1s, sys: 1min 1s, total: 2min 3s
Wall time: 2min 52s

Plot

In [35]:
%%time
%%memit -c
print(datetime.datetime.now())
print('# Compute ∆AOU = OUR * ∆Age + intercept + residual')
print('## Plot each watermass')
print('### Plot')

#____________________
# Load data2plot

savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder.pckl'
with open(savefile, 'rb') as file:
    data2plot1 = pickle.load(file)

#
savefile = dirout + 'data2plot_AOUtrend_OURxAgetrend_reminder_each_watermass.pckl'
with open(savefile, 'rb') as file:
    data2plot2 = pickle.load(file)

#
watermass='southern dense significant'

#____________________
# Set figname for saving figure

figname = dirout + f'plot_AOUtrend_OURxAgetrend_reminder_'+watermass.replace(' ', '_')+'.png'

#--------------------
# Figure parameters
#--------------------

# esm_list = ['MPI-ESM1-2-LR', 'ACCESS-ESM1-5', 'IPSL-CM6A-LR', 'MIROC-ES2L', 'NorESM2-LM']
esm_list = data2plot['ESM']
section_list = ['∆AOU', '∆Age', 'reminder']

zfact_glob = 1e-18 * 117/170 * 12.01 # mmol-O -> PgC
zfact_section = 1

#______________
# Labels
labels_dict = {
    'Global ∆AOU': r'Global $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep ∆AOU': r'Deep $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    'Deep significant ∆AOU': r'Deep significant $\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆AOU'       : r'$\frac{\mathrm{dAOU}}{\mathrm{dt}}$',
    '∆Age'       : r'$\mathrm{OUR} \times \frac{\mathrm{dAge}}{\mathrm{dt}}$',
    'reminder'   : r'$\mathrm{Age} \times \frac{\mathrm{dOUR}}{\mathrm{dt}}$',
    # 'reminder'  : r'Reminder',
}

#______________
# barplot colors
barplot_color_dict = {
    'Global ∆AOU': 'lightgrey',
    '∆AOU': 'grey',
    '∆Age' : cmcrameri.cm.batlowS.colors[0],
    'reminder' : cmcrameri.cm.batlowS.colors[1]
}

#______________
# barplot kwargs
kwbar = dict(linewidth=0.5, align='center')
kwbar1 = dict(linewidth=.5, align='center', edgecolor='k')

#______________
# Sections axis labeling and limits
xlabel = 'Latitude [Deg. N]'
ylabel = 'Depth [m]'
xlim = (-90, 90)
ylim = (6000, -10)

#______________
# Section cmap
a = cmcrameri.cm.vik
colors = a(np.linspace(0,1,10+2))
newcmap = ListedColormap(colors[1:-1])
newcmap.set_over(colors[-1])
newcmap.set_under(colors[0])
newcmap.set_bad("none")

#______________
# Section pcm kwargs
kwpcm = dict(cmap=newcmap, vmin=-.2, vmax=.2)

#______________
# panel labeling
import string
alphabet = list(string.ascii_lowercase)
alphabet_reverse = alphabet[::-1]
# Create a list of double letter combinations (aa, ab, ..., zz)
double_letters = [a + b for a in string.ascii_lowercase for b in string.ascii_lowercase]
extended_alphabet = alphabet + double_letters
extended_alphabet_reverse = extended_alphabet[::-1]

# Pop one now to save a) for the barplot at the top
extended_alphabet_reverse.pop()

#______________
# Set figure
ncol = 3
nrow = len(esm_list)
fwidth_cm = 4
fheight_cm = 3
fsize = (fwidth_cm*cm2in*ncol, fheight_cm*cm2in*nrow) # width, height
fig, ax = plt.subplots(nrows=nrow, ncols=ncol, figsize=fsize, sharex='col', sharey='row')

#--------------------
# Plot sections
#--------------------

pcm = np.zeros_like(ax)
for iesm, vesm in enumerate(esm_list): 
    
    for iii, vvv in enumerate(section_list): 

        zwdata = data2plot2['xavg'][vvv][watermass][iesm]

        zax = ax[iesm, iii]
        xxx = zwdata['xxx']
        yyy = zwdata['yyy']
        ccc = zwdata['ccc'] * zfact_section
        # ccc = np.where(ccc!=0, ccc, np.nan)
        zwpcm = zax.pcolormesh(xxx, yyy, ccc, **kwpcm)
        pcm[iesm, iii] = zwpcm
        zax.set_ylim(ylim)
        zax.set_xlim(xlim)

        zwtxt = extended_alphabet_reverse.pop() + f') '
        zax.text(.02, .98, zwtxt, transform=zax.transAxes, ha='left', va='top')

    #
#

#--------------------
# Decorate sections
#--------------------

fig.tight_layout()

for iesm, vesm in enumerate(esm_list) : 
    zax = ax[iesm, 0]
    zax.text(-.2, 1.2, esm_name_dict[vesm], transform=zax.transAxes, ha='right', va='top', rotation=30.)
#

for zax in ax[:, 0]: zax.set_ylabel(ylabel)
for zax in ax[-1, :]: zax.set_xlabel(xlabel)
        
for iii, vvv in enumerate(section_list): 
    zax = ax[0, iii]
    zax.text(.5, 1.2, labels_dict[vvv], transform=zax.transAxes, ha='center', va='bottom')
#

#---------------------
# Colorbar
#---------------------

zw1 = ax[-1, 0].get_position()
zw2 = ax[-1, -1].get_position()
nx0 = zw1.x0 + .5*zw1.width
ny0 = zw2.y0 - .8*zw1.height
nw  = zw2.x0 + .5*zw2.width - nx0
nh  = 0.05*nw
cax = fig.add_axes([nx0, ny0, nw, nh])
cbar = fig.colorbar(pcm.flat[0], cax=cax, orientation='horizontal', \
                    ticklocation='bottom', extend='both')
clabel = '[mmol$\,$O$_2$ m$^{-3}$ yr$^{-1}$]'
cbar.set_label(clabel)

#--------------------
# Add barplot
#--------------------

zw1 = ax[0, 0].get_position()
zw2 = ax[0, -1].get_position()
nx0 = zw1.x0
ny0 = zw1.y1 + .8*zw1.height
nw  = zw2.x1 - nx0
nh  = 1.5*zw1.height
zax = fig.add_axes([nx0, ny0, nw, nh])

#_________________
# Get x-ticks and xxx and www0 and www1
xxx = np.arange(len(esm_list))

www0 =  0.95
www1 =  0.7

#_________________
# ∆AOU

var = '∆AOU'

hhh = np.array(data2plot1['Global'][var]['total']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Global ∆AOU', color=barplot_color_dict['Global '+var], edgecolor=barplot_color_dict['Global '+var], **kwbar)

hhh = np.array(data2plot1['Global'][var]['deep']) * zfact_glob
zax.bar(xxx, hhh, width=www0, label='Deep ∆AOU', color='none', edgecolor='k',  **kwbar)

# hhh = np.array(data2plot['Global'][var]['deep significant']) * zfact_glob
# zax.bar(xxx, hhh, width=www0, color='none', label='Deep significant ∆AOU', linewidth=.5, ls='--', edgecolor='red', align='center')

hhh = np.array(data2plot2['Global'][var][watermass]) * zfact_glob
zax.bar(xxx, hhh, width=www1, label=var, color=barplot_color_dict[var], edgecolor=barplot_color_dict[var], **kwbar)

nnn = 2
www2=www1/(nnn+1)

#_________________
# ∆Age
iii = 0
var = '∆Age'
hhh = np.array(data2plot2['Global'][var][watermass]) * zfact_glob
bbb = 0*hhh
xxx1 = xxx-(nnn-(1+2*iii))*www2/2
bars = zax.bar(xxx1, hhh, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)

#_________________
# Reminder
iii = 1
var = 'reminder'
bbb += hhh
hhh = np.array(data2plot2['Global'][var][watermass]) * zfact_glob
xxx2 = xxx-(nnn-(1+2*iii))*www2/2
zax.bar(xxx2, hhh, bottom=bbb, width=www2, color=barplot_color_dict[var], label=var, **kwbar1)


#--------------------
# Decorate barplot
#--------------------

zax.axhline(0, c='k', lw=.5)

#____________________
# Set labels and title

ttl = 'a) Spatially integrated trends'
zax.set_title(ttl, loc='left')
zax.set_ylabel(r'[Pg$\,$C yr$^{-1}$]')
x_tick_labels = [esm_name_dict[vesm] for vesm in esm_list]
zax.set_xticks(xxx)
zax.set_xticklabels(x_tick_labels)

#____________________
# Adjust axis limits

ylim = zax.get_ylim()
dylim = 0.30*(ylim[1]-ylim[0])
ylim2 = (ylim[0]-dylim, ylim[1]+dylim)
zax.set_ylim(ylim2)

xlim = zax.get_xlim()
dxlim = 0.05*(xlim[1]-xlim[0])
xlim2 = (xlim[0]-dxlim, xlim[1])
zax.set_xlim(xlim2)


#____________________
# legend
aaa=zax.get_legend_handles_labels()
handles = aaa[0]
labels = [labels_dict[vvv] for vvv in aaa[1]]
zax.legend(handles, labels, frameon=False, handletextpad=1, ncol=2,\
           loc='upper left', bbox_to_anchor=(0, 1))

#--------------------
# Add R2 text
#--------------------
ylim = zax.get_ylim()
xlim = zax.get_xlim()

iii=0
ytext = ylim[0]+0.005*(ylim[1]-ylim[0])
for ind, val in enumerate(data2plot2['Global']['R2'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val:.2f}', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'$\overline{R^2}$:', ha='left', va='bottom', size=6)

#--------------------
# Add volume text
#--------------------
iii=0
ytext = ylim[0]+0.1*(ylim[1]-ylim[0])
totvolume = data2plot1['Global']['volume']['total']
for ind, val in enumerate(data2plot2['Global']['volume'][watermass]): 
    xtext = xxx[ind]
    zax.text( xtext, ytext, f'{val/totvolume[ind]*100:.0f}%', ha='center', va='bottom', size=6)
#
xtext = xlim[0]+0.005*(xlim[1]-xlim[0])
zax.text(xtext, ytext, r'Vol. ratio:', ha='left', va='bottom', size=6)

#--------------------
# Add share of dAge
#--------------------
iii=0
ytext = 0.005*(ylim[1]-ylim[0])
daou = data2plot2['Global']['∆AOU'][watermass]
for ind, val in enumerate(data2plot2['Global']['∆Age'][watermass]): 
    xtext = xxx1[ind]
    zax.text( xtext, ytext, f'{val/daou[ind]*100:.0f}%', c='white', ha='center', va='bottom', size=6)
#

#--------------------
# Print share of dAge
#--------------------
print('---- dAGE/dAOU ----')
daou = data2plot2['Global']['∆AOU'][watermass]
dage = data2plot2['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot2['ESM']): 
    print(f'{val}: {dage[ind]/daou[ind]*100:.0f}%')
#
print('-----------------')

#--------------------
# Print dAOU, dAge
#--------------------
print('---- dAGE, dAOU ----')
daou = data2plot2['Global']['∆AOU'][watermass]
dage = data2plot2['Global']['∆Age'][watermass]
for ind, val in enumerate(data2plot2['ESM']): 
    print(f'{val}: {dage[ind]*zfact_glob:.2f}, {daou[ind]*zfact_glob:.2f} [PgC/yr]')
#
print('-----------------')

#--------------------
# Save figure
#--------------------

print('Save figure...')
fig.savefig(figname, bbox_inches='tight')
print('Figure saved: '+figname)
2025-03-24 18:49:24.171572
# Compute ∆AOU = OUR * ∆Age + intercept + residual
## Plot each watermass
### Plot
<string>:121: UserWarning: The input coordinates to pcolormesh are interpreted as cell centers, but are not monotonically increasing or decreasing. This may lead to incorrectly calculated cell edges, in which case, please supply explicit cell edges to pcolormesh.
---- dAGE/dAOU ----
MPI-ESM1-2-LR: 95%
ACCESS-ESM1-5: 54%
IPSL-CM6A-LR: 116%
MIROC-ES2L: 95%
NorESM2-LM: 103%
-----------------
---- dAGE, dAOU ----
MPI-ESM1-2-LR: 0.20, 0.21 [PgC/yr]
ACCESS-ESM1-5: 0.14, 0.25 [PgC/yr]
IPSL-CM6A-LR: 0.28, 0.25 [PgC/yr]
MIROC-ES2L: 0.38, 0.40 [PgC/yr]
NorESM2-LM: 0.16, 0.16 [PgC/yr]
-----------------
Save figure...
Figure saved: 24-12-17-figures-for-storyline-v1/plot_AOUtrend_OURxAgetrend_reminder_southern_dense_significant.png
peak memory: 8995.00 MiB, increment: 4435.81 MiB
CPU times: user 2.4 s, sys: 948 ms, total: 3.35 s
Wall time: 2.53 s
In [ ]: